; USER20.AZM 12-04-87 ; A replacement USER.ASM for Northstar Advantage by R. Gaspari. ; Provides SMT no-slot clock call and provides POP-UP display. ; Assemble with Z80MR. Use SYSGEN and DDT to overlay onto bios. USORG EQU 0F400H ; start of USER.ASM ; (F400 for floppy, E700 for harddisk) OU EQU 0 ; 0 for normal SYSGEN use. Set to offset ; value if code is loaded at run time. ROW EQU 0 COL EQU 50 ; location of pop-up clock display CA1 EQU 1BH ; cursor addressing sequence CA2 EQU '=' CA3 EQU ROW+20H CA4 EQU COL+20H CI1 EQU 19H ; make cursor invisible (byte 1) CI2 EQU 01H ; byte 2 CV1 EQU 18H ; cursor visible code (byte 1) CV2 EQU 02H ; byte 2 ORG USORG ; ; JUMP VECTOR (SEMI-TRADITIONAL) ; USERORG EQU $+OU ; ORIGIN FOR REFERENCE JP INIT ; INITIALIZATION CODE JP CONST ; CONSOLE READY TEST JP CONIN ; CONSOLE INPUT JP CONOUT ; CONSOLE OUTPUT JP LIST ; LIST DEVICE JP LIST ; PUNCH ## (deleted to make room) JP TIME ; READER (deleted, used for time) JP LISTST ; LIST DEVICE READINESS TEST ; ; CONCLUDE VECTOR WITH BAUD RATES ; THE PRINTER RATE (BAUDA) IS ALTERED BY CPMGEN BAUDA EQU $+OU ; Start-up baud rate for Adv slot 1 DEFB 112 ; 112 = 1200 baud BAUDB EQU $+OU ; Start-up baud rate for slot 2 DEFB 126 ; 126 = 9600 baud BAUDC EQU $+OU ; Startup for slot 3 DEFB 120 ; 127=19200; 124=4800; 120=2400; 96=600 ; ; ********** END OF VECTOR *********************** ENDVECT EQU $ ; MARK FOR LATER ORIGIN ; ; ; ***** ENTRY POINTS INTO "UPPER" BIOS *********************** ; ; ORG USERORG+200H ; JUST BEYOND USER AREA ; ACTUAL CONSOLE ROUTINES ARE ENTERED ABOVE USER AREA CONSLI EQU USERORG+200H ; ACTUAL KEYBOARD INPUT ROUTINE CONSLO EQU USERORG+200H+3 ; ACTUAL KEYBOARD OUTPUT ROUTINE ; ; MACHINE REGISTERS MAP0,MAP1,MAP2,CONTROL,TOP-SCAN-LINE ; ARE MANAGED THROUGH THE ROUTINES BELOW: ; ENTER WITH REG C=REG# 0 TO 4 RESPECTIVELY ; VALUE RETURNED/SUPPLIED IN REG B RDREG EQU USERORG+200H+6 ; READ MACHINE REGISTER WRREG EQU USERORG+200H+9 ; WRITE MACHINE REGISTER ; ; MUST SERVICE OFTEN WHILE HUNG IN I/O WAIT LOOPS: OFTENS EQU USERORG+200H+12 ; CALL TO SERVICE OFTEN ; ROUTINE TOGGLES SPEAKER CONE SPTOG EQU USERORG+200H+15 ; TOGGLE SPEAKER ; ; ; ************ BOARDLET I/O PORT DEFINITIONS *********** ; ; THE CP/M LIST DEVICE IS DEFINED AS PORT "A" ; IT IS HERE ASSIGNED TO BOARDLET 1 PORTAB EQU 1 ; BOARDLET 1 PORTA EQU (6-PORTAB)*16 ; BEGINNING OF BLOCK PORTAT EQU 76H-PORTAB ; INTERROGATE FOR TYPE ; ; THIS INITIALIZES ADVANTAGE SLOT 2 PORTBB EQU 2 ; BOARDLET 2 PORTB EQU (6-PORTBB)*16 ; BEGINNING OF BLOCK PORTBT EQU 76H-PORTBB ; SERIAL OR PARALLEL? ; ; THIS INITIALIZES ADVANTAGE SLOT 3 PORTCB EQU 3 ; BOARDLET 3 PORTC EQU (6-PORTCB)*16 ; FIRST PORT IN BOARDLET PORTCT EQU 76H-PORTCB ; DETERMINES BOARLET TYPE ; ; ; WHEN THE PORT TYPE IS INTERROGATED, VALUES INPUT ARE: BLTSER EQU 0F7H ; BOARDLET IS SERIAL BLTPAR EQU 0DBH ; BOARDLET IS PARALLEL ; ******************************************************* ; ORG ENDVECT ; RESUME AT ABOVE MARKED LOCATION ; ; ; ; *** CONSOLE STATUS TEST FOR DATA READY CONST EQU $+OU CALL CLKPOP ; if pop-up clk in effect, display it CALL 0F60Ch ; SERVICE OFTEN FOR SCROLL, ETC. IN A,(0D0H) ; INTERROGATE KEYBOARD STATUS AND 040H ; TEST KEYBOARD READY BIT RET Z ; NO DATA, RETURN WITH A=0 CONS2: LD A,0FFH ; DATA WAITING, SEND FF RET CLKPOP EQU $+OU LD A,(CLKFLG) ; see if pop-up flag is set OR A RET Z ; if no, return to call clkpop LD HL,CLKCTR ; if yes, get the delay counter INC (HL) ; display clk only every 256 iterations RET NZ ; if NZ, go back to call clkpop JP CDSP ; otherwise, display clock CLKFLG EQU $+OU DB 0 ; set 0FFh for continuous pop-up display CLKCTR EQU $+OU DB 0 ; temp storage for 256 state counter ; *** CONSOLE INPUT ## Complete overhaul of CONIN routine. ## CONIN EQU $+OU ; relocated conin, coninl is local CONINL: CALL CONST ; check status (again) JR Z,CONINL ; loop until status says we're ready CALL 0F600H ; get the byte using upper bios CP 9BH ; see if it's CMND-F1 JR NZ,CN9B ; (single pop-up clock request) CALL CDSP ; if yes, display clock JR CONINL ; and go back for next input CN9B: CP 9CH ; see if it's CMND-F2 JR NZ,CN9C ; (continuous pop-up clk display) LD A,(CLKFLG) ; prepare to toggle the pop-up flag CPL ; complement the accumulator LD (CLKFLG),A ; store the toggled flag JR CONINL ; go back for next input CN9C: CP 7FH ; see if it's a "delete" RET NZ ; if not, return with it the input byte LD A,7FH ; if yes, get the 7F replacement RET ; and return with it ; *** CONSOLE OUTPUT CONOUT EQU $+OU JP 0F603H ; SO SEND CHAR TO CONSOLE ; ; ; ***** BOARDLET I/O HANDLING ************************** ; ***** CP/M'S "LIST" DEVICE - USES "PORTA" DEFINED ABOVE ; LIST EQU $+OU LISTX: CALL LISTST ; USE STATUS TEST BELOW JR Z,LISTX ; LOOP UNTIL READY IN A,(PORTAT) ; DETERMINE TYPE OF PORT CP BLTSER ; SEE IF SERIAL JR Z,LISTSER ; YES, SKIP INSTRUCTION BELOW OUT (PORTA+4),A ; RESET PO-FLAG LISTSER: LD A,C ; STAGE BYTE INTO A OUT (PORTA),A ; SEND IT RET ; AND EXIT ; ; *** LIST DEVICE STATUS TEST *** USES PORTA LISTST EQU $+OU CALL OFTENS ; SERVICE OFTEN FOR SCROLL LD B,1 ; READY BIT FOR SERIAL PORT IN A,(PORTAT) ; DETERMINE TYPE OF PORT A CP BLTSER ; SEE IF SERIAL JR Z,LTTSER ; YES SKIP NEXT INSTR LD B,4 ; PARALLEL PORT READY MASK LTTSER: IN A,(PORTA+1) ; INTERROGATE CONTROL PORT AND B ; VS. READY MASK RET Z ; DONE, Z=NOT READY OR 0FFH ; READY, MAKE ALL ONES RET ; ;***** CP/M'S "READER" DEVICE - Code deleted to make room ;READER ; ;***** CP/M "PUNCH" DEVICE - Code deleted to make room ;PUNCH ; ; ****** INITIALIZATION ROUTINE ******************* INIT EQU $+OU ; INITIALIZATION XOR A ; INITIALIZE BYTE 3 & 4 ; LD (3),A ; SET THE "IOBYTE" TO "STANDARD" LD (4),A ; SET CURRENT DISK TO A: & USER=0 ; INITIALIZE "BOARDLET"S FOR SERIAL OR PARALLEL I/O ; START WITH PORT A - NORMALLY THE PRINTER LD A,(BAUDA) ; STAGE BAUD RATE IN CASE SERIAL LD D,A ; IN D REGISTER IN A,(PORTAT) ; TEST PORT A TYPE LD HL,INITAS ; SERIAL INIT STRING LD BC,5*256+PORTA+1 ; B=5 COUNT, C=UART CMD PORT CALL INITCOM ; USE COMMON ROUTINE IN A,(PORTA) ; CLEAR ANY JUNK JR NZ,INITB0 ; DONE IF NOT PARALLEL OUT (PORTA+5),A ; SET PO-FLAG FOR PARALLEL PORT ; ; INITIALIZE ADV SLOT 2 INITB0: LD A,(BAUDB) ; STAGE BAUD RATE IN CASE SERIAL LD D,A ; IN D REGISTER IN A,(PORTBT) ; GET BOARD TYPE LD HL,INITBS ; SERIAL INIT STRING LD BC,5*256+PORTB+1 ; B=5 COUNT, C=UART CMD PORT CALL INITCOM IN A,(PORTB) ; CLEAR ANY JUNK JR NZ,INITC0 ; DONE IF NOT PARALLEL OUT (PORTB+6),A ; RESET PI-FLAG ; INITIALIZE ADV SLOT 3 INITC0: LD A,(BAUDC) ; STAGE BAUD RATE IN CASE SERIAL LD D,A ; IN D REGISTER IN A,(PORTCT) ; GET PORT C TYPE LD HL,INITCS ; SERIAL INIT STRING LD BC,5*256+PORTC+1 ; B=5 COUNT, C=UART CMD PORT CALL INITCOM IN A,(PORTC) ; CLEAR ANY JUNK JR NZ,INITC9 ; DONE IF NOT PARALLEL OUT (PORTC+5),A ; INIT PO-FLAG ON AS IF JUST ACK'ED INITC9: RET ; END OF BOARDLET INITIALIZATION ; COMMON SUBROUTINE TO TEST BOARDLET TYPE ## Patch to known board type ; IF SERIAL, OUTPUT INITIALIZATION STRING ; REGS MUST BE: A=BOARDLET-TYPE, B=INITSTRING-LENGTH, C=COMMAND PORT ; D=BAUD-RATE-FACTOR, HL@INITSTRING INITCOM EQU $+OU CP BLTSER ; SEE IF SERIAL PORT JR NZ,INITCP ; NOT SERIAL, GO TEST IF PARALLEL DEFB 0EDH,0B3H ; Z80 OTIR INSTRUCTION, SENDS INIT STR LD A,C ; NOW CHANGE I/O PORT ADD A,7 ; FROM UART COMMAND TO BAUD RATE LD C,A ; C=BAUD RATE PORT DEFB 0EDH,51H ; Z80 OUT (C),D (BAUD RATE FROM D) RET ; EXIT (NOTE Z FLAG IS OFF) INITCP: CP BLTPAR ; SEE IF PARALLEL PORT RET ; SO Z ON IFF PARALLEL PORT ; INITIALIZATION STRINGS FOR 8251 USARTS ON SERIAL BOARDLETS INITAS EQU $+OU INITBS EQU $+OU INITCS EQU $+OU DEFB 80H,80H ; FILLERS TO INSURE KNOWN STATE DEFB 40H ; INTERNAL RESET COMMAND DEFB 0CEH ; 2 STOPS, NO PARITY, 8 DATA BITS, 16X CLK DEFB 37H ; RTS, ER, RXE, DTR, TXE COMMAND ;INITBS: DEFB 80H,80H,40H,0CEH,37H ; SAME AS FOR A ;INITCS: DEFB 80H,80H,40H,0CEH,37H ; SAME AS FOR A CSTACK EQU $+OU ; stack grows into init area above DW 0 ; storage for old stack pointer ; **************************************** ; PART IIIb - CLOCK READ ROUTINE ; **************************************** TIME EQU $+OU ; routine returns with pointer in HL DI PUSH IX ; find addr of current fcb POP HL ; put it in accum LD A,H CP 40H ; see if fcb located above 4000h PUSH AF ; @2 save carry flag LD A,84H JR C,TSKIP ; if below 4000h, map high OUT 0A0H,A ; otherwise, map PROM onto 0000 - 4000h LD DE,0004H ; & read memory in the PROM space JR TDONE TSKIP: OUT 0A1H,A ; map PROM onto 4000h - 8000h LD DE,4004H ; & read memory in the PROM space TDONE: LD A,(DE) ; the first read is just for a reset DEC DE ; enable done with 0003h & 0002h LD HL,TIMREG ; 1. - CLOCK ENABLE routine LD A,(HL) ; get first byte of recog sequence TIEBYT: LD B,A ; b contains byte LD C,8 ; do this for 8 bits TIEBIT: SRL B ; 0->b7...b0->carry JR NC,TIEX1 LD A,(DE) ; WRITE with bit A2 low & bit A0 high JR TIEX2 TIEX1: DEC DE LD A,(DE) ; WRITE with bit A2 low & bit A0 low INC DE TIEX2: DEC C JR NZ,TIEBIT ; do all 8 bits INC HL ; point to next byte LD A,(HL) ; get next byte OR A ; set zero flag JR NZ,TIEBYT ; do next byte til code enable complete TIREAD: INC DE ; 2. - CLOCK READ routine LD HL,TIBUF+7 ; set up buffer LD C,8 ; do 8 bytes TIRBYT: LD B,8 ; do 8 bits TIRBIT: LD A,(DE) SRL A ; shift bit into carry RR (HL) ; shift bit into (hl) DEC B ; dec bit counter JR NZ,TIRBIT ; read next bit DEC HL ; point to next buf location DEC C ; dec byte counter JR NZ,TIRBYT ; read next byte LD DE,TIBUF+3 ; point to weekday INC HL ; point to year LD A,(DE) ; get weekday LD B,(HL) ; get year LD (HL),A ; overwrite weekday onto year LD A,B ; get year LD (DE),A ; write year onto weekday LD HL,TIBUF+1 ; point to "month" before RET POP AF ; @2 get carry flag back again LD A,0 JR C,TSKP2 ; OUT 0A0H,A ; map normal RAM onto 0000h - 4000h JR TDON2 TSKP2: INC A ; set A=1 OUT 0A1H,A ; map normal RAM onto 4000h - 8000h TDON2: EI RET ; and return from TIME TIMREG EQU $+OU DB 0c5h,3ah,0a3h,5ch ; comparison reg DB 0c5h,3ah,0a3h,5ch,0 ; definition TIBUF EQU $+OU ; (example) Tues 10/20/87 5:15pm DB 0 ; year (87h) DB 0 ; month (10h) DB 0 ; day (20h) DB 0 ; weekday (03h) [year] DB 0 ; hour (17h) DB 0 ; minutes (15h) DB 0,0 ; seconds (0000) ; **************************************** ; PART IIIc - POP-UP CLOCK DISPLAY ; **************************************** BIOSOUT EQU USORG+0203H ; bios conout for Northstar Adv BIOSCUR EQU USORG+021EH ; current cursor location x & y CDSP EQU $+OU ; clock display code LD (CSTACK),SP ; save orig stack pointer LD SP,CSTACK ; point to local stack LD HL,(BIOSCUR) ; get current cursor location PUSH HL ; save it (@1) CALL TIME ; call TIME & retn with pointer LD B,6 ; convert 6 bcd bytes to ascii LD DE,CDABUF+7 ; point to start of ascii data buffer CDS2: LD A,(HL) ; get the bcd data byte CALL CDASC ; convert it & store at DE & DE+1 INC HL ; point to next bcd data INC DE ; point to next ascii buffer loc DEC B ; countdown 6 bcd bytes JR NZ,CDS2 ; LD HL,CDABUF ; point HL to start of cdabuf LD B,25 ; count 25 bytes to send out CDS4: LD C,(HL) ; get the byte to send to console CALL BIOSOUT ; send out via bios conout INC HL ; point to next loc DEC B ; continue for all 25 bytes JR NZ,CDS4 POP HL ; get orig cursor location LD (BIOSCUR),HL ; put cursor back in bios LD C,CV1 ; code to make cursor visible again CALL BIOSOUT ; send it out via bios LD C,CV2 ; byte 2 of "cursor visible" CALL BIOSOUT ; send it out LD SP,(CSTACK) ; restore orig stack XOR A ; load 0 into A before ret RET ; normal retn from CDSP CDABUF EQU $+OU DB CI1,CI2 ; make cursor invisible DB CA1,CA2,CA3,CA4 ; move to location row & col DB ' ' ; DB 0,0,'/' ; month ascii DB 0,0,'/' ; day ascii DB 0,0,' ' ; year ascii DB 0,0,':' ; hour ascii DB 0,0,':' ; min ascii DB 0,0,' ' ; sec ascii CDASC EQU $+OU ; bcd to ascii conversion LD C,A ; save incoming byte for later AND 0F0H ; mask upper nibble RLCA ; shift right 4 times RLCA ; (or left 4 times) RLCA ; RLCA ; CALL CDAS2 ; convert and store LD A,C ; get byte back AND 0FH ; mask lower nibble CDAS2 EQU $+OU OR 30H ; convert to ascii LD (DE),A ; store it INC DE ; move to next loc for next nibble RET ; retn CDAS2 & retn CDASC UREF2 EQU $+OU ; location of end (after it's moved) ; must end before F5BAh end ; USER20.AZM