;************************************************* ;* * ;* BIGBOARD MONITOR ROM, NON-RELOCATABLE VERSION * ;* 61 K version * ;* Original outline: Russell Smith, 2-Aug-80 * ;* Rewritten by Trevor Marshall, Feb-Mar 81 * ;* for self-contained WAIT-AID BIOS * ;* * ;************************************************* ; ; TRUE EQU -1 FALSE EQU 0 ; IS5MHZ EQU TRUE ROM: EQU FALSE SIO.POLLED EQU TRUE SIO.INTERRUPT EQU NOT SIO.POLLED PARALLEL EQU TRUE SERIAL EQU FALSE CACHE EQU TRUE ; ROMLOC: EQU 0F000H ;START OF 2K ROM ; ORG ROMLOC-10H ; at 0 on reset,100 if disk ; DI IF NOT ROM LD HL,110H ;Where the code will be loaded ENDIF ;NOT ROM IF ROM LD HL,10H ;Where code is in ROM ENDIF ;ROM LD DE,ROMLOC ;Where we want it put LD BC,0FF0H ;remaining Length of this ROM ;and the next LDIR JP INIT ;Initialize all parameters NOP ;To use up 16 bytes ; ;************************************************ ;* * ;* COLD START INITIALIZATION ROUTINE FOR * ;* CONFIGURING THE SYSTEM AFTER A POWER-ON * ;* OR PUSHBUTTON RESET. * ;* 18-Oct-80 * ;* * ;************************************************ ; ; ; CP/M Equates CPMBASE EQU 0D800H ;CP/M base address CCP EQU CPMBASE ;CONSOLE COMMAND PROCESSOR BDOS EQU 806H+CPMBASE ;OPERATING SYSTEM ENTRY POINT CBIOS EQU 1600H+CPMBASE ;BASE OF CUSTOM BIOS ; ; -- MONITOR ENTRY POINT TABLE -- ; ; We need to catch all the external calls to the BIOS ; so that we can restore registers upon error ; and handle stack imbalance from error control ; BIOS FUNCTION CALLING TABLE COLD: JP COLDBOOT ;COLD JP WARMBOOT ;WARM JP CPMST ;CONST JP CPMIN ;CONIN JP CPMOUT ;CONOUT JP LIST ;LIST JP CPMOUT ;PUNCH JP KBDIN ;READER CALL BIOSGO ;HOME CALL BIOSGO ;SELDSK CALL BIOSGO ;SETTRK JP SETSEC ;SETSEC JP SETDMA ;SETDMA CALL BIOSGO ;READ CALL BIOSGO ;WRITE JP LSTST ;LSTST JP SECTRAN ;SECTRAN ; A few extra easily accessible subroutines JP INIT IF ROM JP PROMPT ENDIF ;ROM ;Now we need a jump table for the routines BIOSTBL: JP COLDBOOT JP WARMBOOT JP CPMST JP CPMIN JP CPMOUT JP LIST JP CPMOUT ;Punch JP KBDIN ;Reader JP HOME JP SELECT JP SEEK JP SETSEC JP SETDMA JP READ JP WRITE ; BIOSGO: LD (HLSAV),HL ;Save all registers LD (DESAV),DE LD (BCSAV),BC POP HL ;Get calling address in HL LD A,L ;Arithmetic with L only is OK SUB A,3 ;Point at start of CALL LD L,A ;with HL LD (OFFSET),A ;Save function number LD (SPSAV),SP ; Save Stack Pointer at entry LD DE,BIOSTBL-COLD ;Add the table length to first table ADD HL,DE ;To point to corresponding entry LD (ROUTINE.ADDRESS),HL ;Save it for error rentry LD DE,ERROR.HANDLER ;Fake a return to Error Handler PUSH DE LD DE,(DESAV) ;REstore DE ; Trying JP(IX) BLEW DEBUG JP (HL) ;Go to BIOS routine ERROR.HANDLER: PUSH HL ;Save return status, acc. ;Is error status zero? PUSH AF OR A JR Z,RET9 ;Return if no error indicated CALL PNEXT ;Output an error message DB 0DH,0AH,7,'***** Disk I/O error, trk/sec ',EOT LD A,(TRACK) CALL PUT2HX LD A,'/' CALL CONOUT LD A,(SECTOR) CALL PUT2HX CALL PNEXT DB ', FDC status ',EOT POP AF PUSH AF ;Get status CALL PUT2HS ;Put it in HEX CALL CONIN AND 5FH ;LC -> UC CP 'R' JR Z,TRYAGAIN CP 'I' JR Z,IGNORE ;Else drop through to RET9 and return error status CALL PNEXT DB 'continue',EOT RET9: ;If functions return values in HL, trap the HL restore POP AF POP HL ;get HL PUSH AF ;acc destroyed below LD A,(OFFSET) CP 1BH ;SELDSK JR EQ,RET10 LD HL,(HLSAV) RET10: LD DE,(DESAV) LD BC,(BCSAV) POP AF ;retrieve accumulator ; LD SP,(SPSAV) RET TRYAGAIN: CALL PNEXT DB 'retry',EOT LD A,(TRACK) ;save TRK # LD HL,PARAM1 ;in scratch LD (HL),A INC HL LD A,(SECTOR) LD (HL),A CALL HOME ;Recalibrate the drive ;Disregard any errors here, easier to handle them upon return LD HL,PARAM1 LD C,(HL) CALL SEEK LD A,(PARAM1+1) ;Restore original sector LD (SECTOR),A ;Effectively CALL SETSEC LD BC,(BCSAV) ;Restore relevant calling data LD HL,(ROUTINE.ADDRESS) LD SP,(SPSAV) ;where we began LD DE,ERROR.HANDLER PUSH DE ;Fake a return to the handler JP (HL) ; IGNORE: CALL PNEXT DB 'ignore',EOT ; POP AF XOR A ;make error status = 0 and return PUSH AF JR RET9 ; ; Indirect call to subroutines, modifying code CPMOUT: LD A,C CONOUT: JP CRTOUT ;Modified by SWITCH routines CPMIN: CONIN: JP KBDIN CPMST: CONST: JP KBDST ; LSTST: IF NOT ROM IN A,PIOBDAT ;Is printer busy? CPL AND 2 ;if LO, busy RET Z ;if so ret Zero ENDIF ;NOT ROM LD A,0FFH RET ; LIST: IF NOT ROM PUSH AF LP1Z: CALL LSTST JR Z,LP1Z POP AF OUT PIOADAT,A IN A,PIOBDAT ;Strobe port B bit 7 RES 7,A OUT PIOBDAT,A SET 7,A OUT PIOBDAT,A ENDIF ;NOT ROM RET ; ;The CCP jumps to coldboot if there is an boot error COLDBOOT: XOR A LD (0003H),A ;RESET IOBYTE TO ZEROS ;CCP uses location 4 as a store for user & drive LD (4),A ;Select User 0 & A: on COLDboot JR GOCPM ; ; WARMBOOT: LD SP,STACK ; We will select in the CCP ROM by writing ; a 1 to port 1C bit 7 IN A,(BITDAT) SET 7,A OUT (BITDAT),A ; the PROM is in socket 3, 1000H up LD BC,800H ;move prom contents LD DE,CCP LD HL,1000H LDIR ; IN A,(BITDAT) ;Deselect PROM RES 7,A OUT (BITDAT),A ; ;We must zero the bottom bytes of BDOS ;So that the serial numbers match LD B,6 XOR A LD HL,BDOS-6 ;or CPMBASE + 800H LPP3: LD (HL),A INC HL DJNZ LPP3 ; ;Zero all the allocation vector tables GOCPM: LD BC,VECTORS.LENGTH-1 LD DE,ALLOCATION.VECTORS+1 LD HL,ALLOCATION.VECTORS LD (HL),A LDIR ; Zero our BIOS logged disks CALL LOGOFF ; LD A,0C3H ;STORE JUMP VECTORS IN RAM LD (00H),A LD HL,CBIOS+3 ;JUMP TO CBIOS WARM BOOT AT 00H LD (01H),HL LD (05H),A LD HL,BDOS ;JUMP TO BDOS GOES AT 05H LD (06H),HL IF ROM LD (38H),A LD HL,BKPT ;JUMP TO Breakpoint GOES AT 38H LD (39H),HL ENDIF ;ROM LD HL,FILTER ;Jump to filter replaces that in BDOS LD (BDOS+1),HL LD BC,0080H CALL SETDMA ;MAKE DISK BUFFER=0080H ;The CCP takes in C | User# | Drive# | LD A,(4) ;Get current user/drive value LD C,A ;into C for CCP ; JP CCP ; ; LOGOFF: ;Log off both disks LD HL,UNIT ;Point to TRKTAB-1 LD A,-1 ;'CLEAR' it LD (HL),A INC HL LD (HL),A INC HL LD (HL),A ;=== 2 Disk system RET ; ;******************************************************** ;* Entry point at initialization * ;******************************************************** ; ;DO A SHORT POST-RESET TIME DELAY. ALSO INITIALIZES THE ;STACK POINTER AND ZEROES THE MONITOR SCRATCH RAM. INIT: DI LD HL,MEM ;POINT TO START OF MONITOR RAM INIT1: LD (HL),0 ;FILL 256 BYTE SPACE WITH ZEROS LD SP,HL ;DO SOMETHING USEFUL & ADD DELAY INC L ;On exit SP points to top of RAM JR NZ,INIT1 ;LOOP TAKES ABOUT 4 msecs ; ; INITIALIZE THE Z-80 FOR INTERRUPT MODE #2 LD A,H LD I,A ;LOAD I REG WITH MSB OF VECTOR TABLE IM 2 ; AND SELECT INTERRUPT MODE 2 ; CALL CLRSCN ;FILL THE CRT MEMORY WITH BLANKS IN A,(BITDAT) ;Deselect CRT Bank RES 7,A OUT (BITDAT),A ; ; STORE ANY NON-ZERO VALUES FOR VARIABLES IN MEMORY ; LD HL,INTAB ;POINT TO DEFAULT VARIABLE TABLE INIT2: LD B,0 LD C,(HL) ;BC=DATA BLOCK BYTECOUNT INC HL LD E,(HL) ;DE=DESTINATION FOR DATA INC HL LD D,(HL) INC HL LDIR ;COPY DATA @ HL TO VARIABLES @ DE BIT 7,(HL) JR Z,INIT2 ;LOOP AGAIN IF NOT AT END OF TABLE ; ; INITIALIZE THE PROGRAMMABLE I/O DEVICES LD DE,110H ;For copyright routine ; INC HL ;POINT TO I/O INIT DATA TABLE INIT3: LD B,(HL) ;B=INIT LOOP BYTECOUNT INC HL LD C,(HL) ;C=DEVICE CONTROL PORT# INC HL OTIR ;SEND DATA @ HL TO PORT @ C BIT 7,(HL) ;TEST FOR TABLE END MARKER JR Z,INIT3 ;LOOP AGAIN IF NOT AT END ; ; LD BC,80H ; CALL SETDMA ; ;Now check the copyright integrity IF ROM LD B,47 ;length of msg LD HL,COPYRT-110H ADD HL,DE XOR A LPP1 ADD (HL) INC HL DJNZ LPP1 LPP2 CP 0E5H ;patch for each serial # JR NZ,LPP2 ENDIF ;ROM ;DETERMINE IF CONSOLE I/O CONFIGURATION WILL BE FOR THE ;ON-BOARD CRT AND KEYBOARD OR AN EXTERNAL SERIAL TERMINAL ; ;First enable keyboard interrupts IN A,(KBDDAT) ;Clear any rubbish LD A,10000011B OUT (KBDCTL),A ;ENABLE INTERRUPTS FROM KBD PIO EI ; LD C,SIOCPB LD A,2 ;Select read register 2 OUT (C),A IN A,(C) ;TEST SIO READ REGISTER 2 TO see ; CP 00000110B ; IF THE SIO IS INSTALLED (Status affected) CP 0 ;See if SIO is installed (no status affects) JR NZ,PARALL ;SKIP CONFIGURATION TEST IF NO SIO LD B,00010000B ;B=RESET SIO EXTERNAL STATUS COMMAND DECIDE: OUT (C),B ;TEST FOR ARRIVAL OF A SERIAL IN A,(C) ; INPUT CHARACTER START BIT BIT 4,A JR NZ,BAUD ;EXIT LOOP IF START BIT DETECTED CALL KBDST ;See if FIFO has char ready from JR Z,DECIDE ; parallel keyboard yet PARALL: CALL KBDIN ;DISCARD FIRST KEYBOARD CHARACTER JR SIGNON ; ; ; AUTOMATIC BAUD RATE SETTING ROUTINE FOR SIO ; BAUD: XOR A BAUD1: OUT (C),B IN D,(C) ;READ SIO STATUS REGISTER BIT 4,D ;TEST THE SYNC/HUNT BIT JR Z,BAUD1 ;LOOP UNTIL IT CHANGES STATE BAUD2: INC A OUT (C),B ;RESET REGISTER #0 FLAGS AGAIN IN D,(C) ;AND LOOP TIMING THE SYNC/HUNT BIT BIT 4,D JR NZ,BAUD2 ;REPEAT UNTIL BIT CHANGES AGAIN IF IS5MHZ RRA ;divide by 2 ENDIF ;IS5MHZ LD HL,RATES-1 BAUD3: INC HL ;INDEX INTO BAUD RATE TABLE RLA ; USING COUNT DERRIVED IN A JR NC,BAUD3 LD A,(HL) ;GET BAUD RATE CONTROL BYTE FROM OUT (BAUDB),A ;TABLE AND OUTPUT TO COM-8116 TIMER CALL SIOIN ;DISARD FIRST SERIAL INPUT CHARACTER IF SIO.INTERRUPT LD A,1 OUT (SIOCPB),A ;RE-PROGRAM SIO B TO GENERATE LD A,00011000B ;INTERRUPTS ON RECIEVED DATA, OUT (SIOCPB),A ;PARITY DOES NOT AFFECT VECTOR LD HL,SIOOUT LD DE,KBDIN LD BC,KBDST ENDIF ;SIO.INTERRUPT IF SIO.POLLED LD HL,SIOOUT LD DE,SIOIN ;No interrupts on input LD BC,SIOST ENDIF ;SIO.POLLED CALL SWITCH.STASH ;And modify vectors for VDU ; ; PRINT SIGNON MESSAGE ; SIGNON: IF NOT ROM CALL PNEXT DB '(C) Copyright 1981 Trevor Marshall',EOT XOR A LD (UNIT),A ;So that warmboot logs in A: JP WARMBOOT ENDIF ;NOT ROM IF ROM DEFB 9,9,9,'... WAITAID monitor 4.1 ...' DEFB CR,LF,LF,9,9 COPYRT: DB '(C) Copyright 1981 Trevor Marshall, serial # 00 ',EOT ;Now delay for Drive ready signal to go true LD HL,2000H LP1X: DEC HL EX (SP),HL EX (SP),HL LD A,H OR A,L ;Is HL zero? JR NZ,LP1X LD C,0 ;Drive A CALL SELECT AND 10011000B JR NZ,ABORT.BOOT JP BOOT ;Boot if ready ABORT.BOOT: CALL LOGOFF ;Log off DSK not ready JP PROMPT ;Else GO ENTER MONITOR ENDIF ;ROM ; ; ; BAUD RATE CONSTANTS FOR COM 8116 BAUD RATE GENERATOR ; RATES: DEFB 0101B ; 300 BAUD DEFB 0110B ; 600 BAUD DEFB 0111B ; 1200 BAUD DEFB 1010B ; 2400 BAUD DEFB 1100B ; 4800 BAUD DEFB 1110B ; 9600 BAUD DEFB 1111B ; 19200 BAUD DEFB 1111B ; 19200 BAUD ; ; INTAB EQU $ ;INITIALIZATION DATA TABLES ; ; ;INITIALIZE THE Z-80 'I' REGISTER INTERRUPT VECTOR TABLE ; DEFB 2 DEFW SYSVEC+2 DEFW KEYSRV ;PARALLEL KEYBOARD INTERRUPT VECTOR ; DEFB 2 DEFW CTCVEC+6 DEFW TIMER ;ONE SECOND TIMER INTERRUPT VECTOR ; IF SIO.INTERRUPT DEFB 4 DEFW SIOVEC+4 DEFW SIOINT ;SIO RECIEVE INTERRUPT VECTOR DEFW SIOERR ;SIO PARITY, OVERRUN & FRAMING ERROR ; ENDIF ;SIO.INTERRUPT ; ; INITIALIZE DISK I/O DRIVER VARIABLES ; DEFB 6 DEFW UNIT DEFB 255 ;FLAG ALL DRIVES AS DE-SELECTED DEFB 255,255 ;CLEAR HEAD POSITION TABLE DEFB 0,0 ;Both default single density DEFB 30 ;SET MOTOR TURN-OFF TIMER ; RECLEN, TRACK & SECTOR not initialized ; ; INITIALIZE THE CRT DISPLAY CURSOR ; DEFB 2 DEFW CHRSAV DEFB ' ' DEFB '_'+80H ;USE BLINKING UNDERSCORE ; ; SET DEFAULT 'SOFTWARE' INTERRUPT VECTORS ; DEFB 6 DEFW TIKVEC DEFW DSKTMR ;POINT 'TIKVEC' TO DISK MOTOR TIMER DEFW STASH ;POINT 'PINVEC' TO FIFO INPUT ROUTINE DEFW STASH ;POINT 'SINVEC' TO FIFO INPUT ROUTINE ; ; DEFB -1 ;END OF VARIABLE INIT TABLE ; ; ; BAUDA EQU 00H ;CHANEL A BAUD RATE GENETATOR SIO EQU 04H ;DUAL SERIAL I/O GENPIO EQU 08H ;GENERAL PURPOSE PARALLEL I/O BAUDB EQU 0CH ;CHANEL B BAUD RATE GENERATOR WD1771 EQU 10H ;WESTERN DIGITAL DISK CONTROLLER SCROLL EQU 14H ;CRT SCROLL MEMORY SCROLL REGISTER CTC EQU 18H ;QUAD COUNTER/TIMER CIRCUIT SYSPIO EQU 1CH ;SYSTEM PARALLEL I/O ; ;INITIALIZE SYSTEM PIO FOR USE AS BANK-SWITCH, ; DISK DRIVE SELECT AND PARALLEL KEYBOARD INPUT ; BITDAT EQU SYSPIO+0 BITCTL EQU SYSPIO+1 KBDDAT EQU SYSPIO+2 KBDCTL EQU SYSPIO+3 IF ROM DEFB 3,BITCTL DEFB 11001111B ;PUT SYSTEM PIO IN BIT MODE DEFB 00000000B ;All bits outputs DEFB 01000000B ;DISABLE INTERRUPTS ; DEFB 1,BITDAT DEFB 00001000B ;DE-SELECT ROMS, ENABLE DRIVE 0 ; ; DD* line high ; ; SIDE 2 line low (=side 1) ENDIF ;ROM ; DEFB 2,KBDCTL DEFB 01001111B ;PUT KEYBOARD PORT IN INPUT MODE DEFB [SYSVEC+2] AND 0FFH ;LOAD KEYBOARD INTERRUPT VECTOR ; ;Set up printer PIO PIOADAT: EQU 8 PIOACTL: EQU 9 PIOBDAT: EQU 10 PIOBCTL: EQU 11 ; IF PARALLEL AND NOT ROM DB 3,PIOACTL DB 3 ;Disable interrupts DB 0CFH ;Put in bit mode DB 0 ;All outputs ; DB 3,PIOBCTL DB 3 ;Disable interrupts DB 0CFH ;Bit mode DB 0FH ;Bits 1-3 inputs ; DB 1,PIOBDAT ;Set bit 7 for parallel printer DB 80H ;reset special bell (bit 6) ENDIF ;PARALLEL ; ; INITIALIZE CHANELS 2 AND 3 OF THE CTC ; TO GENERATE ONE SECOND INTERRUPTS FROM CTC3 ; CTC0 EQU CTC+0 ;CTC CHANEL 0 PORT# CTC1 EQU CTC+1 ;CTC CHANEL 1 CTC2 EQU CTC+2 ;CTC CHANEL 2 CTC3 EQU CTC+3 ;CTC CHANEL 3 DEFB 1,CTC0 DEFB [CTCVEC] AND 0FFH ;BASE INTERRUPT VECTOR FOR CTC ; IF ROM DEFB 2,CTC2 DEFB 00100111B ;PUT CTC2 IN TIMER MODE DEFB 105 ;CTC2 PERIOD=105*256*400 nsecs ; DEFB 2,CTC3 DEFB 11000111B ;PUT CTC3 IN COUNTER MODE DEFB 93 ;CTC3 PERIOD=999936 MICROSECONDS ENDIF ;ROM ; ; ;INITIALIZE SIO CHANEL B FOR ASYNCHRONOUS SERIAL ; INTERFACE TO PRINTER OR TERMINAL ; SIODPA EQU SIO+0 ;SIO DATA PORT A SIODPB EQU SIO+1 ;SIO DATA PORT B SIOCPA EQU SIO+2 ;SIO CONTROL/STATUS PORT A SIOCPB EQU SIO+3 ;SIO CONTROL/STATUS PORT B IF ROM DEFB 1,BAUDB DEFB 0101B ;SET COM 8116 FOR 300 BAUD DEFAULT DEFB 10,SIOCPB DEFB 4 ;SELECT REGISTER #4 DEFB 01001100B ;16X CLOCK, 2 STOP BIT, NO PARITY DEFB 1 ;SELECT REGISTER #1 DEFB 00000000B ;STATUS NO EFFECT ON VECTOR, NO INTERRUPTS DEFB 3 ;SELECT REGISTER #3 DEFB 11000001B ;8 BITS/RX CHARACTER,NO AUTO ENABLES DEFB 5 ;SELECT REGISTER #5 DEFB 11101010B ;8 BITS/TX CHARACTER, ASSERT DTR DEFB 2 ;SELECT REGISTER #2 DEFB [SIOVEC] AND 0FFH ;LOAD INTERRUPT VECTOR BASE ELSE DB 4,SIOCPB DB 1 DB 0 ;status no effect DB 2 DB [SIOVEC] AND 0FFH ENDIF ;ROM DEFB -1 ;END-OF-TABLE ; ; INIT DONE ; ; ;************************************************ ;* * ;* BASIC HEX MONITOR FOR Z-80 PROCESSORS * ;* 3-Aug-80 * ;* * ;************************************************ ; IF ROM ;Only want the monitor in ROM ; ; ; PROMPT: LD SP,[MEM+0FFH] CALL PNEXT DEFB CR,LF DEFB '* ' DEFB EOT LD HL,LINBUF LD C,32 CALL GETLIN ;INPUT A BUFERED CONSOLE LINE JR C,WHAT ;PRINT 'WHAT ?' IF INPUT ERROR XOR A LD (ESCFLG),A CALL CRLFS LD A,(LINBUF) ;GET FIRST CHARACTER IN LINE CP CR JR Z,PROMPT ;JUMP IF A NULL LINE LD HL,CMDTAB ;SEARCH FOR A MATCHING CHARACTER LD BC,CMDSIZ/3 ; IN COMMAND SEARCH TABLE CALL SEARCH JR NZ,WHAT ;TRY AGAIN IF SEACRH FAILS PUSH BC LD IY,LINBUF+1 CALL PARAMS ;INPUT NUMERIC PARAMETERS FROM POP IX ; LINE BUFFER AND TEST IF ERROR JR C,WHAT LD HL,(PARAM1) LD DE,(PARAM2) LD BC,(PARAM3) CALL CALLX ;CALL SUBROUTINE @ IX JR NC,PROMPT ;GO BACK TO PROMPT IF NO ERRORS WHAT: CALL PNEXT DEFB ' what ?' DEFB 'G'-64 ;SAY 'what ?' AND BEEP THE BELL DEFB EOT JR PROMPT ; ; CALLX: JP (IX) ;CALL SUBROUTINE @ IX ; ; ; CMDTAB: DEFB 'R' DB 'W' DEFB 'O' DEFB 'E' DEFB 'G' DEFB 'T' DEFB 'F' DEFB 'S' DEFB 'M' DEFB 'B' DEFB 'D' DEFB 'S' DEFW SWITCH ;SWITCH CONSOLE OUTPUT VECTOR DEFW MEMDMP ;DUMP MEMORY IN HEX/ASCII DEFW BOOT ;BOOT UP CP/M DEFW BLOCK ;MEMORY BLOCK MOVE DEFW VIEW ;MEMORY EXAMINE/CHANGE DEFW FILL ;FILL MEMORY DEFW TEST ;RAM DIAGNOSTIC DEFW GOTO ;JUMP TO MEMORY LOCATION DEFW INCMD ;READ FROM INPUT PORT DEFW OUTCMD ;WRITE TO OUTPUT PORT DW WRITE.SECTOR DEFW READ.SECTOR ;DISPLAY DISK SECTOR DATA CMDSIZ EQU $-CMDTAB ; ; ;************************************************ ;* * ;* MONITOR COMMAND ACTION ROUTINES PACKAGE * ;* * ;************************************************ ; ; ; ; ; ; -- DISK BOOT LOADER COMMAND -- ; BOOT: LD C,0 ;SELECT DRIVE 0 FOR BOOT LOAD CALL SELECT JR NZ,DSKERR CALL LOGIN ;HOME HEAD TO TRACK 0 and login disk JR NZ,DSKERR ;ERROR IF NOT READY OR AT TR0 LD BC,0080H ;POINT TO CP/M READ BUFFER CALL SETDMA LD C,1 ;SELECT SECTOR 1 CALL SETSEC CALL READ ;READ TRACK 0/ SECTOR 1 JR NZ,DSKERR JP 0080H ;GO EXECUTE LOADER ; ; ; ---- Write Disk Sector----- WRITE.SECTOR: LD C,A ;Save param count LD A,0FFH LD (RWFLAG),A ;If flag is non zero = write JR RW11 ; -- DISK SECTOR READ COMMAND -- ; READ.SECTOR: LD C,A ;Save param count XOR A LD (RWFLAG),A ; flag is zero if read RW11: LD A,C CP 3 ;CHECK PARAMETER COUNT SCF RET NZ CALL LOGOFF ;Logoff current disk LD HL,PARAM1 LD C,(HL) ;USE FIRST ARG AS UNIT# CALL SELECT JR NZ,DSKERR LD HL,PARAM2 LD C,(HL) ;USE SECOND ARG AS TRACK# CALL SEEK JR NZ,DSKERR LD HL,PARAM3 LD C,(HL) ;USE THIRD ARG AS SECTOR# CALL SETSEC LD BC,0080H ;** Default DSK buffer for BOOT CALL SETDMA LD A,(RWFLAG) OR A JR Z,RD1 CALL WRITE JR NZ,DSKERR ;Will need to catch this during LOGON RET RD1: CALL READ JR NZ,DSKERR ; LD HL,0080H ; LD DE,8 ; JP DUMP ;DUMP DISK READ BUFFER AND RETURN RET ; ; DSKERR: LD C,A ;SAVE 1771 STATUS CALL PNEXT DEFB 'disk error ',EOT LD A,C ;get status CALL PUT2HS RET ; ; ; -- MEMORY DUMP COMMAND -- ; MEMDMP: DEC A ;CHECK PARAMETER COUNT JR Z,MDMP2 DEC A JR Z,MDMP3 MDMP1: LD HL,(LAST) MDMP2: LD DE,8 ;Default # of lines dumped JR MDMP3B MDMP3: EX DE,HL SBC HL,DE ;DERRIVE BYTECOUNT FOR DUMP RANGE LD B,4 MDMP3A: SRL H ;DIVIDE BYTECOUNT BY 16 RR L DJNZ MDMP3A INC HL EX DE,HL MDMP3B: CALL DUMP ;DUMP DE*16 BYTES STRTING AT HL LD (LAST),HL RET ; ; DUMP: PUSH HL ;SAVE STARTING ADDRESS CALL PUT4HS ;PRINT STARTING ADDRESS IN HEX CALL SPACE LD B,16 DUMP2: LD A,(HL) ;GET A DATA BYTE @ HL INC HL CALL PUT2HS ;PRINT THE DATA IN HEX DJNZ DUMP2 ;REPEAT 16 TIMES POP HL ;RESTORE STARTING ADDRESS LD B,16 DUMP3: LD A,(HL) ;GET BACK DATA BYTE @ HL INC HL RES 7,A CP 20H JR C,DUMP4 CP 7FH JR C,DUMP5 DUMP4: LD A,'.' ;PRINT A DOT IF DATA < 20 OR > 7F DUMP5: CALL OUTPUT ;PRINT ASCII CHARACTER IN A DJNZ DUMP3 CALL CRLFS RET NZ ;EXIT IF ESCAPE REQUEST IS INDICATED DEC DE LD A,D OR E JR NZ,DUMP RET ; ; ; ; ; -- MEMORY EXAMINE COMMAND -- ; VIEW: CALL MDATA CALL ECHO CP CR JR Z,VIEW4 CP '-' JR Z,VIEW5 VIEW2: CALL ASCHEX CCF RET NC RLCA RLCA RLCA RLCA LD C,A CALL ECHO CALL ASCHEX CCF RET NC OR C VIEW3: LD (HL),A CALL CHECK VIEW4: INC HL INC HL VIEW5: DEC HL JR VIEW ; ; ; ; -- JUMP TO MEMORY LOCATION COMMAND -- ; ; We should fill the registers from RAM GOTO: DEC A ;CHECK PARAMETER COUNT SCF RET NZ PUSH HL POP IX ;Set up the jump at 30H for breakpoints (F7H,RST6) LD A,0C3H LD HL,30H LD (HL),A INC HL LD A,[BKPT AND 0FFH] LD (HL),A INC HL LD A,[BKPT SHR 8] LD (HL),A ; OK, now go to routine JP (IX) ; OR A ; RET ;RETURN IF WE GET BACK AGAIN ; BKPT: PUSH HL PUSH DE PUSH BC PUSH AF CALL PNEXT DB '**Breakpoint**, regs are: ',EOT POP AF CALL PUT2HS ;Put acc POP HL CALL PUT4HS ;BC POP HL CALL PUT4HS ;DE POP HL CALL PUT4HS ;HL CALL PNEXT DB ',PC=',EOT POP HL DEC HL ;To allow for PC incr past RST30 CALL PUT4HS ;PC CALL CRLFS JP PROMPT ; ; ; -- MEMORY READ/WRITE DIAGNOSTIC COMMAND -- ; TEST: CP 2 ;CHECK PARAMETER COUNT SCF RET NZ INC DE LD E,D ;GET ENDING PAGE ADDRESS INTO E LD D,H ;GET STARTING PAGE ADDRESS INTO D LD B,0 ;INITIALIZE PASS COUNTER TEST1: LD H,D ;POINT HL TO START OF BLOCK LD L,0 TEST2: LD A,L XOR H ;GENERATE TEST BYTE XOR B LD (HL),A ;STORE BYTE IN RAM INC HL LD A,H CP E ;CHECK FOR END OF TEST BLOCK JR NZ,TEST2 ; NOW READ BACK EACH BYTE & COMPARE LD H,D LD L,0 ;POINT HL BACK TO START TEST3: LD A,L XOR H ;RE-GENERATE TEST BYTE DATA XOR B CALL CHECK ;VERIFY MEMORY DATA STILL GOOD RET NZ ;EXIT IF ESCAPE REQUEST IS INDICATED INC HL ; ELSE GO ON TO NEXT BYTE LD A,H CP E ;CHECK FOR END OF BLOCK JR NZ,TEST3 INC B ;BUMP PASS COUNT LD A,'+' CALL OUTPUT ;PRINT '+' AND ALLOW FOR EXIT JR Z,TEST1 ;DO ANOTHER PASS IF NO ESCAPE RET ; ; ; CHECK: CP (HL) RET Z ;RETURN IF (HL)=A PUSH AF CALL MDATA ;PRINT WHAT WAS ACTUALLY READ CALL PNEXT DEFB 'should=' DEFB EOT POP AF JP PUT2HS ;PRINT WHAT SHOULD HAVE BEEN READ ; ; MDATA: CALL CRLFS CALL PUT4HS LD A,(HL) JP PUT2HS ; ; ; ; -- FILL MEMORY WITH CONSTANT COMMAND -- ; FILL: CP 3 ;CHECK IF PARAMETER COUNT=3 SCF RET NZ FILL1: LD (HL),C PUSH HL OR A SBC HL,DE ;COMPARE HL TO END ADDRESS IN DE POP HL INC HL ;ADVANCE POINTER AFTER COMPARISON JR C,FILL1 RET ; ; ; ; ; -- MEMORY BLOCK MOVE COMMAND -- ; BLOCK: CP 3 ;CHECK IF PARAMETER COUNT=3 SCF RET NZ CALL BLOCAD LD A,C OR B RET Z ;EXIT NOW IF BC=0 LDIR RET ; ; ; BLOCAD: EX DE,HL OR A ;CLEAR CARRY SBC HL,DE ;GET DIFFRENCE BETWEEN EX DE,HL ;HL & DE FOR BYTECOUNT PUSH DE PUSH BC POP DE ;GET OLD BC INTO DE POP BC INC BC ;GET COUNT+1 INTO BC RET ; ; ; ; ; -- READ FROM INPUT PORT COMMAND -- ; INCMD: DEC A ;CHECK IF PARAMETER COUNT=1 SCF RET NZ LD C,L ;POINT C TO INPUT PORT IN1: CALL CRLFS LD A,C CALL PUT2HS IN A,(C) CALL PUT2HS CALL ECHO CP CR JR Z,IN2 CP '-' JR Z,IN3 OR A RET IN2: INC C INC C IN3: DEC C JR IN1 ; ; ; ; -- WRITE TO OUTPUT PORT COMMAND -- ; Syntax: O OUTCMD: CP 2 ;CHECK IF PARAMETER COUNT=2 SCF RET NZ LD C,E ;POINT C TO OUTPUT PORT OUT (C),L ;OUTPUT DATA PASSED IN L OR A RET ; ; ; -- SWITCH CONSOLE OUTPUT DEVICE COMMAND -- ; SWITCH: LD HL,COFLAG INC (HL) ;TOGGLE CONSOLE OUTPUT TYPE FLAG BIT 0,(HL) IF SIO.POLLED LD DE,SIOIN ;No interrupts LD BC,SIOST LD HL,SIOOUT ELSE LD DE,KBDIN ;Interrupts LD BC,KBDST LD HL,SIOOUT ENDIF ;SIO.POLLED JR Z,SWIT2 ;JUMP IF ZERO TO ONE TRANSITION LD HL,CRTOUT LD DE,KBDIN LD BC,KBDST ENDIF ;ROM (over all the monitor package) SWITCH.STASH: ;Store the Serial/Parallel vectors SWIT2: LD (CONOUT+1),HL ;STORE NEW CONSOLE OUTPUT ADDRESS LD (CONIN+1),DE LD (CONST+1),BC RET ; ; ;************************************************ ;* * ;* CONSOLE I/O PACKAGE AND UTILITY ROUTINES * ;* * ;************************************************ ; IF ROM ; ; GETLIN: LD B,C ;SAVE MAX LINE LENGTH PARAMETER IN B GLIN1: CALL ECHO ;GET A CHARACTER FROM THE CONSOLE CP CR ;CHECK FOR CARRIAGE RETURN JR Z,GLIN2 CP 'H'-64 ;CHECK FOR CTL-H BACKSPACE JR Z,GLIN4 ; Check for delete here will not work, catch it at CHIN ; CP 7FH ;Check fo delete ; JR Z,GLIN4 CP ' ' RET C ;OTHER CONTROL CHARACTERS ARE ILLEGAL LD (HL),A INC HL ;STORE CHARACTER IN BUFFER DEC C JR NZ,GLIN1 ;GET ANOTHER IF THERE'S MORE ROOM SCF RET ;RETURN WITH CARRY=1 IF TOO ;MANY CHARACTERS ARE ENTERED GLIN2: LD (HL),A ;PUT CARRIAGE RETURN ON END OF LINE RET ;RETURN WITH CARRY BIT=0 GLIN4: DEC HL ;DELETE LAST CHARACTER FROM BUFFER CALL PNEXT DEFB ' ','H'-64 ;PRINT A SPACE TO OVERWRITE THE DEFB EOT ; LAST CHARACTER, THEN DO A BACKSPACE INC C LD A,B ;MAKE SURE YOU'RE NOT TRYING TO SUB C ;BACKSPACE PAST THE START OF THE LINE JR NC,GLIN1 RET ; ; ; PARAMS: LD BC,0 LD A,(IY+0) LOOKAGAIN: CP CR ;CHECK IF LINE TERMINATES JR Z,ZEROLENGTH ;Immediately with CP ' ' ;Or begins with a space JR Z,DROPIT CP 'M' ;Or an M JR Z,DROPIT JR PARA2 DROPIT: INC IY LD A,(IY) JR LOOKAGAIN ; ZEROLENGTH: XOR A RET ;RETURN WITH PARAM COUNT=0 IF SO ; PARA1: INC C INC C BIT 3,C SCF RET NZ ;ERROR IF > 4 NUMBERS ENTERED PARA2: PUSH BC ;SAVE PARAMETER COUNT CALL GETHEX ;READ A NUMBER FROM LINE BUFFER POP BC PARA4: RET C ;ERROR IF RESULT OVER 16 BITS LD IX,PARAM1 ;POINT TO PARAMETER STORAGE AREA ADD IX,BC ;ADD PARAMETER COUNT IN BC LD (IX+0),L LD (IX+1),H ;STORE DATA RETURNED FROM 'GETHEX' CP ' ' JR Z,PARA1 ;GET ANOTHER ITEM IF SPACE CP ',' JR Z,PARA1 ;GET ANOTHER ITEM IF COMMA CP CR SCF ;ELSE CHECK FOR CARRIAGE RETURN RET NZ ; AND EXIT WITH CY=1 IF NOT PAREND: LD A,C SRL A ;A=COUNT OF NUMBERS ENTERED INC A RET ; ; GETHEX CONVERTS ASCII TO BINARY AND DOES ; HIGH LIMIT CHECKS TO LESS THAN 17 BITS. ; CARRY SET ON ILLEGAL CONVERSION RESULT ; TERMINATING CHARACTER RETURNS IN A. ; HL RETURNS WITH 16 BIT BINARY INTEGER ; GETHEX: LD HL,0 JR GNUM3 GNUM1: LD B,4 GNUM2: ADD HL,HL ;MULTIPLY RESULT BY 16 RET C ;RETURN IF IT OVERFLOWS 16 BITS DJNZ GNUM2 LD E,A ;APPEND NEW LOW ORDER DIGIT LD D,0 ;AND GET RESULT BACK INTO DE ADD HL,DE RET C ;RETURN IF OVERFLOW GNUM3: LD A,(IY+0) ;GET A CHARACTER FROM LINE INPUT INC IY ; BUFFER @ IY AND BUMP IY LD C,A CALL ASCHEX ;CONVERT ASCII TO NUMERIC JR NC,GNUM1 LD A,C OR A RET ; ; ASCHEX: SUB '0' RET C CP 10 CCF RET NC SUB 7 CP 10 RET C CP 16 CCF RET ; ENDIF ;ROM ; ; SEARCH: CPIR ;SEARCH TABLE @HL FOR MATCH WITH A RET NZ ;EXIT NOW IF SEARCH FAILS ADD HL,BC ADD HL,BC ;ADD RESIDUE FROM CPIR BYTECOUNT ADD HL,BC ; TO HL 3 TIMES TO GET POINTER LD C,(HL) ; TO ADDRESS PART OF TABLE ENTRY INC HL LD B,(HL) RET ;EXIT WITH Z=1 TO INDICATE MATCH ; ; ; PUT4HS: LD A,H CALL PUT2HX LD A,L PUT2HS: CALL PUT2HX JR SPACE ; ; PUT2HEX: PUT2HX: PUSH AF RRA RRA RRA RRA CALL PUTNIB POP AF PUTNIB: AND 00001111B ADD A,90H DAA ADC A,40H DAA JR OUTPUT ; ; ; PMSG PRINTS THE STRING OF ASCII CHARACTERS ; POINTED TO BY THE RELATIVE ADDRESS IN DE ; UNTIL AN EOT IS ENCOUNTERED IN THE STRING. ; EOT EQU 04H CR EQU 0DH LF EQU 0AH ; PNEXT: EX (SP),HL CALL PMSG EX (SP),HL RET ; PMSG: LD A,(HL) INC HL CP EOT RET Z CALL OUTPUT JR PMSG ; ; ; CRLFS OUTPUTS A RETURN-LINEFEED-SPACE ; TO THE CONSOLE DEVICE ; CRLFS: CALL PNEXT DEFB CR,LF,EOT SPACE: LD A,' ' JR OUTPUT ; IF ROM ; ; ; ECHO INPUTS ONE CHARACTER FROM THE CONSOLE ; DEVICE, PRINTS IT ON THE CONSOLE OUTPUT AND ; THEN RETURNS IT IN REGISTER A WITH BIT 7 RESET ; ; OUTPUT PRINTS THE CHARACTER IN REGISTER A ON ; THE CONSOLE OUTPUT DEVICE AND THEN DOES A CHECK ; FOR CONSOLE INPUT TO FREEZE OR ABORT OUTPUT. ; ECHO: CALL CONIN ;INPUT A CHARACTER AND ECHO IT CP 7FH ;delete? JR NE,ECHO2 LD A,8 ;yes, load a backspace ECHO2: PUSH AF CALL CONOUT POP AF CP 'Z'+1 ;make LC UC RET C SUB 20H ;CONVERT UPPER CASE TO LOWER CASE RET ; ENDIF ;ROM ; ; OUTPUT: CALL CONOUT CALL CONST ;SEE IF CONSOLE INPUT IS PENDING JR Z,OUTP2 CALL CONIN CP CR ;SEE IF CARRIAGE RETURN WAS TYPED JR Z,OUTP1 CP 'S'-40H ;? JR NZ,OUTP3 ;If not put the char back on the queue CALL CONIN ;WAIT FOR ANOTHER INPUT CHARACTER JR OUTP2 ; THEN RETURN TO CALLING ROUTINE OUTP1: LD (ESCFLG),A ;SET ESCAPE FLAG TO NON-ZERO VALUE OUTP2: LD A,(ESCFLG) OR A ;RETURN CURRENT STATUS OF ESCAPE RET ; FLAG TO CALLING ROUTINE ; OUTP3: PUSH HL CALL REPLACE POP HL JR OUTP2 ;and return ; ; ;******************************************************** ;* * ;* INTERRUPT SERVICE ROUTINES FOR KEYBOARD * ;* INPUT AND REAL-TIME CLOCK FUNCTIONS * ;* 3-Aug-80 * ;* * ;******************************************************** ; ; ; ; KBDST: LD A,(FIFCNT) ;GET INPUT FIFO BYTECOUNT OR A ;TEST IF EQUAL ZERO RET Z ;EXIT WITH A=0 IF QUEUE IS EMPTY LD A,255 RET ;ELSE SET A=255 TO INDICATE DATA READY ; ; ; KBDIN: CALL KBDST JR Z,KBDIN ;LOOP UNTIL KEYBOARD INPUT READY PUSH HL CALL REMOVE ;GET CHARACTER FROM INPUT QUEUE POP HL BIT 7,A ;If B7 set it must be HONEYWELL KYBD char code RET Z ; Else catch the cursor codes for Honeywell keyboard ; 81=UP, 83=right, 84=left, 82=down, 88= home ; E7=CLRSCREEN is caught in STASH immediately it is pressed PUSH HL PUSH BC LD HL,TBL1 ;Point at keycode table LD BC,TBL1.LENGTH CPIR ;Until match or no match JR NZ,RET1 ;Abort if no match LD BC,TBL1.LENGTH ;Point into next table ADD HL,BC LD A,(HL) RET1: POP BC POP HL RET ; TBL1: DB 81H DB 83H DB 84H DB 82H DB 0FFH ;a dummy entry for debugging TBL1.LENGTH: EQU $-TBL1 DB 0 ;1 byte spare as HL is incremented past match TBL2: DB 05H ;Up line DB 04H ;Right char DB 13H ;Left char DB 18H ;Down line DB 0 ;our dummy entry again ;HOME is not processed, as not used by WORDSTAR ; ; ; ; We don't need software shift lock STASH:; LD HL,LOCK ;POINT TO SHIFT LOCK VARIABLES ; CP (HL) ;TEST IF A=SHIFT LOCK CHARACTER ; INC HL ; THEN POINT TO LOCK FLAG ; JR NZ,STASH2 ;JUMP IF NOT THE SHIFT CHARACTER ; INC (HL) ; ELSE COMPLIMENT THE SHIFT LOCK ; RET ; AND EXIT NOW ; STASH2:; BIT 0,(HL) ;TEST THE SHIFT LOCK FLAG ; JR Z,STASH3 ;JUMP IF SHIFT LOCK NOT SET ; CP 40H ;ELSE CHECK FOR SHIFABLE CHARACTER ; JR C,STASH3 ; AND JUMP IF NOT EQUAL OR GREATER ; CP 7FH ; THAN '@' AND LESS THAN RUBOUT ; JR NC,STASH3 ; XOR 00100000B ;ELSE TOGGLE BIT 5 OF THE CHARACTER STASH3: LD C,A ; CP 0E7H ;Honeywell Keyboard's CLEAR SCREEN key JR NZ,NXT35 CALL CLRSCN IN A,(BITDAT) RES 7,A OUT (BITDAT),A RET ; NXT35: LD HL,FIFCNT ;BUMP INPUT FIFO CHARACTER COUNT LD A,(HL) INC A CP 16 RET NC ;EXIT NOW IF FIFO IS FULL LD (HL),A ; ELSE INCREMENT FIFO COUNT LD HL,FIFIN ;POINT HL TO FIFO INPUT OFFSET CALL INDEX LD (HL),C ;STORE CHARACTER IN FIFO @ HL RET ; ; ; ; REMOVE: LD HL,FIFCNT DEC (HL) LD HL,FIFOUT ;POINT HL TO FIFO OUTPUT OFFSET INDEX: LD A,(HL) INC A AND 00001111B ;INCREMENT FIFO POINTER LD (HL),A ; MODULO 16 AND REPLACE LD HL,FIFO ADD A,L ;INDEX INTO FIFO BY OFFSET IN A LD L,A LD A,(HL) RET ; REPLACE: LD HL,FIFCNT INC (HL) LD HL,FIFOUT INDEX2: LD A,(HL) DEC A AND 00001111B LD (HL),A RET ; ; ; SOFTWARE DISK MOTOR TURN-OFF TIMER ROUTINE ; DSKTMR: LD HL,MOTOR ;DECREMENT DISK TURN-OFF TIMER DEC (HL) RET NZ ;EXIT IF NOT TIMED OUT YET IN A,(BITDAT) OR 00000100B ;DISABLE ALL DRIVE SELECTS AND OUT (BITDAT),A ; TURN OFF THE SPINDLE MOTORS RET ; ; ; -- INTERRUPT SERVICE ROUTINE FOR PARALLEL KEYBOARD -- ; KEYSRV: LD (SPSAVE),SP ;SAVE USER STACK POINTER AND LD SP,TMPSTK+16 ; SWITCH TO LOCAL STACK PUSH HL PUSH DE PUSH BC PUSH AF ;SAVE MACHINE STATE IN A,(KBDDAT) ;READ KEYBOARD INPUT PORT CPL LD HL,(PINVEC) ;GET KBD INTERRUPT ROUTINE VECTOR JR DSPTCH ; AND JUMP TO DISPATCH POINT ; ; ; ; -- INTERRUPT SERVICE ROUTINE FOR ONE SECOND TIMER -- ; TIMER: LD (SPSAVE),SP ;SAVE USER STACK POINTER AND LD SP,TMPSTK+16 ; SWITCH TO LOCAL STACK PUSH HL PUSH DE PUSH BC PUSH AF LD HL,(TIKVEC) ;GET CLOCK INTERRUPT ROUTINE VECTOR JR DSPTCH ; AND JUMP TO DISPATCH POINT ; ; ; ; -- SERIAL INPUT INTERRUPT SERVICE ROUTINE FOR SIO -- ; IF SIO.INTERRUPT SIOINT: LD (SPSAVE),SP ;SAVE USER STACK POINTER AND LD SP,TMPSTK+16 ; SWITCH TO LOCAL STACK PUSH HL PUSH DE PUSH BC PUSH AF ;SAVE MACHINE STATE IN A,(SIODPB) ;READ SIO DATA INPUT PORT AND 01111111B LD HL,(SINVEC) ;GET SERIAL INPUT ROUTINE VECTOR ENDIF ;SIO.INTERRUPT DSPTCH: CALL CALLHL ;CALL SUBROUTINE ADDRESSED BY H POP AF POP BC POP DE POP HL LD SP,(SPSAVE) EI ;RE-ENABLE INTERRUPTS AND RETURN RETI ; ; ; -- RX ERROR INTERRUPT SERVICE ROUTINE FOR SIO -- ; ; ARRIVE HERE IF RECIEVE INTERRUPT FROM FRAMING, OVERRUN ; AND PARITY ERRORS. (PARITY CAN BE DISABLED) ; SIOERR: IF SIO.INTERRUPT LD (SPSAVE),SP ;SAVE USER STACK POINTER AND LD SP,TMPSTK+16 ; SWITCH TO LOCAL STACK PUSH AF CALL SIOIN2 ;CLEAR BAD CHARACTER FROM SIO LD A,'G'-64 CALL SIOXMT ;OUTPUT A CTL-G AS A WARNING POP AF LD SP,(SPSAVE) ENDIF ;SIO.INTERRUPT IF NOT SIO.INTERRUPT SIOINT: ENDIF ;NOT SIO.INTERRUPT EI RETI ; ; CALLHL: JP (HL) ; ; ; ; POLLED MODE I/O ROUTINES FOR SIO CHANEL B ; SIOST: IN A,(SIOCPB) ;GET SIO STATUS REGISTER AND 00000001B RET Z ;ACC=0 IF NO DATA AVAILABLE LD A,255 RET ; ; SIOIN: CALL SIOST ;TEST CONSOLE STATUS JR Z,SIOIN ;LOOP UNTIL DATA IS RECIEVED SIOIN2: LD A,00110000B ;RESET STATUS BITS IN SIO FOR OUT (SIOCPB),A ; PARITY/OVERRUN/FRAMING ERRORS, IN A,(SIODPB) ; THEN GET THE INPUT CHARACTER AND 7FH RET ; ; ; We dont need null padding SIOOUT: ;CP ' ' ;TEST FOR CONTROL CHARACTERS ; JR NC,SIOXMT ;JUMP IF PRINTABLE CHARACTER ; CALL SIOXMT ; ELSE SEND CONTROL CHARACTER ; LD A,(NULLS) ; AND THEN SEND NULLS AS PADDING ; INC A ;GET NULL PAD COUNT AND FIX SO ; JR PAD1 ; THAT COUNT=0 SENDS NO NULLS ;PAD: PUSH AF ; XOR A ; CALL SIOXMT ;OUTPUT A NULL TO THE SIO ; POP AF ;PAD1: DEC A ; JR NZ,PAD ;LOOP SENDING NULLS TO SIO ; RET ; ; SIOXMT: PUSH AF SIOX1: IN A,(SIOCPB) AND 00000100B ;TEST TBE STATUS BIT JR Z,SIOX1 POP AF OUT (SIODPB),A ;OUTPUT DATA TO SIO RET ; ; ;************************************************ ;* * ;* MEMORY-MAPPED CRT OUTPUT DRIVER * ;* * ;* Russell Smith 18-August-1980 * ;* * ;************************************************ ; ; CRTMEM EQU 3000H ;Start of 3K CRT memory space CRTBAS EQU CRTMEM SHR 8 ;STARTING PAGE# OF 3K CRT SPACE CRTTOP EQU [CRTMEM+3072] SHR 8 ;ENDING PAGE# OF CRT SPACE ; ; CRTOUT: PUSH AF PUSH HL PUSH DE PUSH BC RES 7,A LD C,A DI ;KEEP THE WOLVES AWAY FOR A WHILE LD (SPSAVE),SP LD SP,TMPSTK+16 ;POINT SP TO TOP OF LOCAL STACK IN A,(BITDAT) SET 7,A ;SELECT ROM/CRT MEMORY BANK OUT (BITDAT),A ; ;FIRST REMOVE THE OLD CURSOR CHARACTER FROM THE SCREEN ; LD HL,CHRSAV ;GET CHARACTER NOW OVERLAYED BY CURSOR LD B,(HL) LD HL,(CURSOR) ;LOAD HL WITH CURSOR POINTER LD A,H AND 00001111B ;A LITTLE INSURANCE THAT HL CAN'T OR CRTBAS ; EVER POINT OUTSIDE THE CRT MEMORY LD H,A LD (HL),B ;REMOVE CURSOR BY RESTORING CHARACTER ; ; PROCESS CHARACTER PASSED IN C ; CALL OUTCH ; ;NOW STORE A NEW CURSOR CHARACTER AT THE CURSOR LOCATION ; LD A,(HL) ;GET CHARACTER AT NEW CURSOR LOCATION LD (CHRSAV),A ;SAVE FOR NEXT TIME 'CRTOUT' IS CALLED CP ' ' ;TEST IF CHARACTER IS A SPACE SET 7,A ;THEN TURN ON BIT 7 TO ENABLE BLINK JR NZ,CRT2 ;JUMP IF CHARACTER IS NON-BLANK LD A,(CSRCHR) ;ELSE GET CHARACTER USED FOR CURSOR CRT2: LD (HL),A ;STORE CHARACTER IN A AS CURSOR MARK LD (CURSOR),HL ;SAVE HL AS CURSOR POINTER LD SP,(SPSAVE) IN A,(BITDAT) RES 7,A ;SWITCH BACK THE LOWER 16K OF RAM OUT (BITDAT),A EI ;INTERRUPTS ARE SAFE AGAIN POP BC POP DE POP HL POP AF RET ; ; ; OUTCH: LD DE,LEADIN LD A,(DE) ;GET LEAD-IN SEQUENCE STATE OR A JP NZ,MULTI ;JUMP IF IN A LEAD-IN SEQUENCE LD A,C ; ELSE PROCESS CHARACTER IN C CP ' ' JR C,CONTRL ;JUMP IF A CONTROL CHARACTER DISPLA: LD (HL),C ; ELSE STORE DISPLAYABLE CHARACTER INC HL ; AND ADVANCE POINTER TO NEXT COLUMN LD A,L AND 01111111B ;EXTRACT COLUMN# FROM HL CP 80 RET C ;EXIT IF NOT PAST COLUMN 79 CALL RETURN ; ELSE DO AUTOMATIC CARRIAGE RETURN CALL LFEED ; AND LINEFEED RET ; ; ; CONTRL: PUSH HL LD HL,CTLTAB ;SEARCH FOR CONTROL CHARACTER LD BC,CTLSIZ/3 ;HANDLING SUBROUTINE IN TABLE CALL SEARCH POP HL RET NZ ;EXIT IF NOT IMPLEMENTED PUSH BC RET ;DO SNEAKY JUMP TO PRESERVE REGISTERS CTLTAB: DEFB '_'-64 DEFB '^'-64 DEFB '['-64 DEFB 'Z'-64 DEFB 'X'-64 DEFB 'Q'-64 DEFB 'M'-64 DEFB 'L'-64 DEFB 'K'-64 DEFB 'J'-64 DEFB 'I'-64 DEFB 'H'-64 DEFB 'G'-64 DEFW BELL ;CTL-G IS THE BELL DEFW BAKSPC ;CTL-H IS CURSOR LEFT DEFW TAB ;CTL-I IS TAB DEFW LFEED ;CTL-J IS CURSOR DOWN DEFW UPCSR ;CTL-K IS CURSOR UP DEFW FORSPC ;CTL-L IS CURSOR RIGHT DEFW RETURN ;CTL-M IS CARRIAGE RETURN DEFW CLREOS ;CTL-Q IS CLEAR TO END-OF-SCREEN DEFW CLREOL ;CTL-X IS CLEAR TO END-OF-LINE DEFW CLRSCN ;CTL-Z IS CLEAR SCREEN DEFW ESCAPE ;CTL-[ IS ESCAPE DEFW HOMEUP ;CTL-^ IS HOME UP DEFW STUFF ;CTL-_ IS DISPLAY CONTROL CHARS CTLSIZ EQU $-CTLTAB ; ; ESCAPE: LD A,1 LD (DE),A ;SET LEAD-IN SEQUENCE STATE RET ; FOR XY CURSOR POSITIONING MODE ; ; STUFF: LD A,4 LD (DE),A ;SET LEAD-IN SEQUENCE STATE RET ; FOR CONTROL CHAR OUTPUT MODE ; ; BAKSPC LD A,L ;CHECK FOR LEFT MARGIN AND 01111111B RET Z ;ABORT IF IN LEFTMOST COLUMN DEC HL ;BACK UP CURSOR POINTER RET ; ; FORSPC: LD A,L ;CHECK FOR RIGHTMOST COLUNM AND 01111111B CP 79 RET NC ;DO NOTHING IF ALREADY THERE INC HL RET ;ELSE ADVANCE THE CURSOR POINTER ; ; TAB: LD DE,8 ;TABS ARE EVERY 8 COLUMNS LD A,L ;GET COLUMN COMPONENT OF AND 01111000B ; PREVIOUS TAB POSITION ADD A,E CP 80 ;EXIT IF NEXT TAB COLUMN WOULD RET NC ; BE PAST THE RIGHT MARGIN LD A,L AND 11111000B ;ELSE INCREMENT THE CURSOR LD L,A ; POINTER FOR REAL ADD HL,DE RET ; BELL: IF NOT CACHE IN A,(BITDAT) SET 4,A ;TOGGLE BIT 5 OF SYSTEM PIO TO OUT (BITDAT),A ;TRIGGER BELL HARDWARE TO SOUND ;Now delay for 120msec PUSH BC LD B,120 ;msec LPX2: LD C,8AH LPX3: NOP NOP NOP DEC C JR NZ,LPX3 DJNZ LPX2 POP BC ;Done delay RES 4,A OUT (BITDAT),A RET ENDIF ;NOT CACHE IF CACHE IN A,PIOBDAT ;We use GPPIO bit 7 RES 7,A OUT PIOBDAT,A ;Now delay for 120msec PUSH BC LD B,120 LPZ2: LD C,8AH LPZ3: NOP NOP NOP DEC C JR NZ,LPZ3 DJNZ LPZ2 POP BC SET 7,A OUT PIOBDAT,A RET ENDIF ;CACHE ; ; RETURN: LD A,L AND 10000000B LD L,A ;MOVE CURSOR POINTER BACK RET ; TO START OF LINE ; ; ; Now clear the screen CLRSCN: IN A,(BITDAT) SET 7,A OUT (BITDAT),A ;Select CRT bank LD HL,CRTMEM PUSH HL LD DE,CRTMEM+1 LD BC,24*128 LD (HL),' ' LDIR ;FILL CRT MEMORY WITH SPACES POP HL ;POINT TO HOME CURSOR POSITION LD A,23 LD (BASE),A ;MAKE BASE LINE# BE 23 AND OUT (SCROLL),A ; STORE IN SCROLL REGISTER RET ; ; CLREOL: PUSH HL ;SAVE CURSOR POINTER LD A,L AND 01111111B ;GET COLUMN# COMPONENT OF LD C,A ; CURSOR POINTER INTO C LD A,80 ;CALCULATE HOW MANY CHARACTERS SUB C ; REMAIN ON CURRENT LINE LD B,A CALL CLR ;CLEAR REST OF LINE @ HL POP HL RET ; ; CLREOS: CALL CLREOL ;CLEAR REMAINDER OF CURRENT ROW PUSH HL LD A,(BASE) LD C,A ;COPY BASE SCREEN ROW# TO C CLRS1: LD A,L RLA LD A,H RLA ;GET ROW# COMPONENT OF HL INTO A AND 00011111B CP C ;SEE IF HL IS AT BOTTOM ROW OF SCREEN JR Z,CLRS2 ; AND LEAVE CLEAR LOOP IF SO CALL DNCSR ;ELSE POINT HL TO NEXT ROW DOWN CALL CLRLIN ; AND FILL THAT LINE WITH SPACES JR CLRS1 CLRS2: POP HL ;RESTORE ORIGINAL CURSOR POINTER RET ; ; UPCSR: LD DE,-128 ;SUBTRACT 1 FROM ROW# COMPONENT ADD HL,DE ; OF CURSOR POINTER IN HL LD A,H CP CRTBAS ;CHECK FOR UNDERFLOW OF POINTER RET NC LD H,CRTTOP-1 ;WRAP CURSOR AROUND MODULO 3K RET ; ; DNCSR: LD DE,128 ;ADD 1 TO ROW# COMPONENT ADD HL,DE ;OF CURSOR POINTER IN HL LD A,H CP CRTTOP ;CHECK FOR OVERFLOW OF POINTER RET C LD H,CRTBAS ;RESET POINTER MODULO 128*24 RET ; ; ; LFEED: LD A,L RLA LD A,H RLA ;EXTRACT ROW# COMPONENT OF HL AND 00011111B LD C,A ;COPY ROW# INTO C FOR SCROLL TEST CALL DNCSR ;MOVE CURSOR TO NEXT ROW DOWN LD A,(BASE) ;TEST IF CURSOR WAS ON BOTTOM ROW CP C ; OF SCREEN BEFORE MOVING DOWN RET NZ ;EXIT IF NOT AT BOTTOM PUSH HL ;ELSE PREP TO SCROLL SCREEN UP CALL CLRLIN ;FILL NEW BOTTOM LINE WITH SPACES ADD HL,HL LD A,H ;GET ROW# COMPONENT OF HL INTO A AND 00011111B LD (BASE),A ;STORE NEW BASE LINE# OUT (SCROLL),A ;NOW SCROLL UP NEW BLANK BOTTOM LINE POP HL RET ; ; CLRLIN: LD A,L AND 10000000B ;POINT HL TO FIRST COLUMN OF ROW LD L,A LD B,80 CLR: LD (HL),' ' ;STORE ASCII SPACES AT ADDRESS IN HL INC HL ; AND INCREMENT HL DJNZ CLR ;REPEAT NUMBER OF TIMES GIVEN BY B RET ; ; HOMEUP: LD C,' ' ;FAKE-OUT CURSOR ADDRESSING ROUTINE JR SETROW ; TO DO HOMEUP ALMOST FOR FREE ; ; MULTI: EX DE,HL ;UNCONDITIONALLY RESET THE LEAD-IN LD (HL),0 ; STATE TO ZERO BEFORE GOING ON EX DE,HL CP 1 JR NZ,M2TST SETXY: LD A,C ;GET SECOND CHAR OF SEQUENCE CP '=' RET NZ ;ABORT SEQUENCE IF NOT '=' LD A,2 LD (DE),A ;MAKE LEADIN=2 NEXT TIME RET M2TST: CP 2 JR NZ,M3TST LD A,3 LD (DE),A ;MAKE LEADIN=3 NEXT TIME SETROW: LD A,(BASE) ;ARRIVE HERE ON THIRD CHARACTER ADD A,C ; OF ESC,'=',ROW,COL SEQUENCE SUB ' '-1 SETR2: SUB 24 JR NC,SETR2 ;MAKE SURE ROW# IS BETWEEN 0 AND 23 ADD A,24 OR CRTMEM SHR 7 ;MERGE IN MSB'S OF CRT MEMORY LD H,A LD L,0 SRL H RR L RET M3TST: CP 3 JR NZ,M4TST SETCOL: LD A,C ;ARRIVE HERE ON FOURTH CHARACTER SUB ' ' ; OF ESC,'=',ROW,COL SEQUENCE SETC2: SUB 80 JR NC,SETC2 ;MAKE SURE COL# IS BETWEEN 0 AND 79 ADD A,80 OR L ;MERGE IN COL# WITH L LD L,A RET M4TST: CALL DISPLA ;DISPLAY THE CONTROL CHARACTER RET ; PASSED IN C ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; FILTER FOR CDOS SYSTEM CALLS ; LAST CHANGE 12:Mar:81 ; ; Written by Steve Hope, Trevor Marshall, ; Wait-Aid, Western Australia ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; FILTER: PUSH AF LD A,C CP 80H ;CP/M 0-80H JR GE,CDOS ;CHK IF CDOS COMPATIBLE CP 2 ;WRITE CONSOL NEEDS EXTRA CARE,A must be kept JR Z,SYSC2 CP 11 ;Console status must return all JR Z,SYSC11 CP 15 ;Open file must zero (DE)+32 (current record) CALL Z,SYSC15 POP AF ;FIX STACK LD (PARAM1),HL ;save temporarily LD (PARAM2),DE LD (PARAM3),BC CALL BDOS+0BH ;Usually E011, Go execute BDOS LD BC,(PARAM3) LD DE,(PARAM2) PUSH AF LD A,C ;Check functions which return values in HL CP 27 ;Max 1.4 FN JR GE,RET67 ;All higher return HL CP 24 ;login code JR EQ,RET67 CP 12 ;Get version JR EQ,RET67 LD HL,(PARAM1) RET67: POP AF RET ; SYSC2: LD (SPSAV),SP ;the CP/M DIR command always bombs LD SP,STACK ;due to stack overflow unless LD A,E ;we give it some help CALL CONOUT ;Destroys nothing LD SP,(SPSAV) POP AF RET ; SYSC11: POP AF CALL CPMST OR A RET Z ;Don't alter HL unless we have to LD L,A ; required for CP/M compatibility RET ; SYSC15: PUSH HL ;This is required LD HL,32 ; for CDOS ADD HL,DE LD (HL),0 POP HL RET ;thence to BDOS ; CDOS: CP 80H ;READ NO ECHO JR Z,RNEC CP 82H ;Set CTL-C abort (it doesn't) JR Z,SYSC82 CP 86H ;FORMAT NAME TO FCB JR Z,FFCB ; CP 8EH ;CRT FUNCTION (XY POS) ; JP Z,CRT CP 89H ;MULTIPLY INTEGERS JP Z,MULT CP 8AH ;DIVIDE INTEGERS JP Z,DIVIDE CP 8BH JP Z,HOME ;Home selected disk CP 8DH ;Get version JP Z,GETVERSION CP 90H ;Read date JR Z,READ.DATE CP 92H ;Read time of day JR Z,READ.DATE ;Return zero, same as 90H CP 96H ;Turn motors off JR Z,MOTORS.OFF POP AF ;FIX STACK JP CALLERR ;ELSE ERROR FOR NOW ; RNEC: ;READ NO ECHO POP AF ;FIX STACK JP CPMIN ;And return with char in acc. ; READ.DATE: POP AF XOR A ; A=0 LD C,A LD B,A ;BC=0 RET ;***Done *** ; MOTORS.OFF: IN A,(BITDAT) OR A,01000100B ;Motors off, drive deselected OUT (BITDAT),A SYSC82: POP AF RET ;;; FFCB: POP AF ;FIX STACK PUSH DE ;SAVE DE LD B,(HL) ;GET FIRST CHAR INC HL LD A,(HL) CP ':' ;CHK IF DRIVE JR NZ,NOCOL INC HL ;PAST COL LD A,7 AND B ;DRV # JR FBYTE NOCOL: DEC HL XOR A ;DEFAULT DRV FBYTE: LD (DE),A INC DE LD B,8 ; NAME1: LD A,(HL) CP '*' JR EQ,FILLWILD CP 21H JR C,FILLL1 INC HL CP '.' JR Z,FILLL1 LD (DE),A INC DE DJNZ,NAME1 JR EX1 FILLWILD: LD A,'?' LD (DE),A INC DE DJNZ FILLWILD JR EX1 FILLL1: LD A,' ' LD (DE),A INC DE DJNZ,FILLL1 EX1: ;;;;;; LD A,'.' CP (HL) JR NZ,EXT INC HL EXT: LD B,3 NAME2: LD A,(HL) CP '*' JR EQ,WILDEXT CP 21H JR C,FILLL2 INC HL CP '.' JR Z,FILLL2 LD (DE),A INC DE DJNZ,NAME2 JR EX2 WILDEXT: LD A,'?' LD (DE),A INC DE DJNZ WILDEXT JR EX2 FILLL2: LD A,' ' LD (DE),A INC DE DJNZ,FILLL2 EX2: ;Done, restore ready for exit XOR A LD (DE),A POP DE RET ;CRT: ; POP AF ;FIX STACK ; XOR A ; CP E ;CANT HANDLE ALL ; JR NZ,CRT3 ; LD A,1AH ;Clearscreen char ; JP CRTOUT ;Neglect all other special fns ;CRT3: PUSH DE ;SAVE DE ; CALL PNEXT ; DB 1BH,'=',EOT ; POP DE ;RECOVER DE ; PUSH DE ;SAVE AGAIN ; LD A,1FH ; ADD E ;ROW ; CALL CONOUT ; POP DE ; PUSH DE ; LD A,1FH ; ADD D ;COL ; CALL CONOUT ; POP DE ; RET ;;;;;;;;;;;;;;;;;;;;;;;;;; MULT: ;DE=HL*DE POP AF ;FIX STACK PUSH BC ;SAVE REGS PUSH HL LD L,16 LD BC,0 MLOOP: EX (SP),HL LD A,1 AND A,E JR Z,M1 PUSH HL ADD HL,BC EX (SP),HL POP BC M1: RR B RR C RR D RR E EX (SP),HL DEC L JR NZ,MLOOP POP HL POP BC RET DIVIDE: ;HL=HL/DE , DE=REMAINDER POP AF ;FIX STACK LD A,D OR A,E RET Z ;DIVIDE BY 0 LD A,D CPL LD D,A LD A,E CPL LD E,A INC DE ;2'S COMP PUSH BC LD B,H LD C,L LD HL,0 LD A,17 DLOOP: PUSH HL ADD HL,DE JR NC,D1 EX (SP),HL D1: POP HL RL C RL B RL L RL H DEC A JR NZ,DLOOP OR A,A RR H RR L EX DE,HL LD H,B LD L,C POP BC RET ;;;;;;;; CALLERR: CALL PNEXT DEFB CR,LF,'Illegal system call ',EOT LD A,C CALL PUT2HEX CALL PNEXT DB ',PC=',EOT POP HL ;GET CALL ADDR PUSH HL ;REt addr back on stack DEC HL ;start DEC HL DEC HL ;NOW PRINT ADDR OF ILLEGAL CALL CALL PUT4HS ;Print calling addres XOR A ;Set up return variable values=0 LD BC,0 LD DE,0 LD HL,0 RET ;Hope program will not bomb ; GETVERSION: POP AF LD BC,0107H ;Version 1.07 RET ; ; ; ;************************************************ ;* * ;* DISK INPUT/OUTPUT DRIVER SUBROUTINE PACKAGE * ;* FOR WESTERN DIGITAL 1771 DISK CONTROLLER * ;* * ;* bullet-proof error recovery added 12-APR-80 * ;* * ;************************************************ ; ; ;EQUATES FOR DISK CONTROLLER PORTS AND COMMAND CODES ; STSREG EQU WD1771+0 ;STATUS REGISTER CMDREG EQU WD1771+0 ;COMMAND REGISTER TRKREG EQU WD1771+1 ;TRACK REGISTER SECREG EQU WD1771+2 ;SECTOR REGISTER DATREG EQU WD1771+3 ;DATA REGISTER ; RDCMD EQU 10001000B ;READ COMMAND WRTCMD EQU 10101000B ;WRITE COMMAND SKCMD EQU 00011100B ;SEEK COMMAND FINCMD EQU 11010000B ;FORCE INTR COMMAND RSTCMD EQU 00001100B ;RESTORE COMMAND HLOAD EQU 00000100B ;RD/WRT HEAD LOAD ENABLE ; RET EQU 0C9H ;SUBROUTINE RETURN INSTR OPCODE NMIVEC EQU 0066H ;THE NON-MASKABLE INTERRUPT IS ;USED FOR DATA SYNCRONIZATION BETWEEN ;THE Z-80 AND 1771 DISK CONTROLLER ; ; ; SETSEC: LD HL,SECTOR LD (HL),C XOR A ;To prevent errors being notified RET ; SETDMA: LD (IOPTR),BC XOR A RET ; ; SECTRAN: EX DE,HL ;ADD TRANSLATION TABLE ADDRESS ADD HL,BC ; PASSED IN DE TO SECTOR# IN BC LD L,(HL) LD H,0 ;LOOKUP PHYSICAL SECTOR NUMBER XOR A RET ; AND RETURN IT IN HL ; ; SELECT: LD HL,0 ;In case of error exit LD A,C ;GET UNIT# PASSED IN C AND CP 2 ; CHECK FOR MAXIMUM VALID# RET NC ;ERROR IF NUMBER > 1 ; LD (SAVE.DRIVE),A ;Stash Unit# for DOSELECT later ; check that it is logged LD HL,TRKTAB ;Calculate trk ptr of requested drive LD A,L ADD C LD L,A ;Now pointing at the modified byte LD A,(HL) ;Has this drive been logged on? CP 0FFH LD A,C ;Get new dsk# to A ready for RET21 JR NE,RET21 ;Only a number of FF indicates not logged ;so if anything else make no hardware changes yet ;If the drive is not logged we had better do so, as ; BDOS expects DPB info on its size CALL DOSELECT RET ; ; DOSELECT: LD A,(SAVE.DRIVE) LD C,A LD HL,UNIT ;POINT HL TO DRIVE SELECT DATA LD A,(HL) ;LOAD A WITH CURRENT UNIT# LD (HL),C ; AND STORE NEW UNIT# FROM C ;Check if drive is already current LD E,A ;Save current # SUB A,C JR Z,RET20 ;If so, exit ; LD D,C ;Save new drive IN A,(BITDAT) AND 11011000B ;MERGE IN NEW DRIVE UNIT# & side 0 OR C ; IN PLACE OF THE CURRENT ONE OUT (BITDAT),A ; TO SELECT THE NEW DISK DRIVE ;Delay turnon until an operation is actually requested ; CALL TURNON ;After selecting drive ; BIT 7,A ;TEST DRIVE NOT READY BIT ; JR Z,SEL2 ; AND CONTINUE IF ITS READY ; LD HL,0 ;for BDOS ; LD A,10000000B ;else RETURN DRIVE-NOT-READY STATUS ; OR A ; RET SEL2: ;We will store the old track number and RECLEN for later LD C,D ;retrieve new drive LD A,E ;retrieve original UNIT byte CP 255 ;TEST IF NO DRIVE HAS BEEN SELECTED ; Else store last drive's parameters JR Z,SEL3 ;YET AND SKIP NEXT SEGMENT IF SO INC HL ;POINT TO HEAD POSITION TABLE ADD A,L ; AND ADD IN OLD UNIT# AS INDEX LD L,A LD A,(TRACK) ;Get current track position LD (HL),A ; AND STORE IN TABLE @ HL LD A,TRKTAB.LENGTH ;RECLENTAB next ADD A,L LD L,A LD A,(RECLEN) LD (HL),A ;Store it in reclen table SEL3: LD HL,RECLENTAB LD A,L ADD A,C ;INDEX INTO TABLE TO GET LD L,A ; RECLEN OF NEW DRIVE LD A,(HL) LD (RECLEN),A LD A,-TRKTAB.LENGTH ;Now get select data ADD A,L LD L,A LD A,(HL) CP 255 ;TEST IF NEW DRIVE HAS EVER BEEN JR NE,NXT111 ; SELECTED AND CALL LOGIN ;Do a LOGIN if not LD HL,0 ;for an error exit RET NZ ;If LOGIN fails leave drive logged off,HL=0 NXT111: LD (TRACK),A ;Success,store the current head position ;Now wait for a while to allow time for head load solenoid IF IS5MHZ LD B,8 ;Loop four times ELSE LD B,4 ENDIF ;IS5MHZ LP124: IN A,CTC3 ;Get current CTC3 value LD C,A ;Save it LP123: IN A,CTC3 CP C ;Loop until it has changed JR NZ,LP123 ;The first may be a short cycle DJNZ LP124 ;the rest will be a full 5.5 msec at 5MHz ; RET20: LD A,(UNIT) ;get current unit RET21: LD H,0 LD L,A ;Calculate DPHB addr ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ;MULTIPLY UNIT# BY 16 LD DE,DPBASE ADD HL,DE ;ADD START ADDRESS OF DHP BLOCK XOR A ;No errors, HL points to DPBASE RET ; ; ; ; We log the disks in under HOME ; Home aways in S/D ; HOME: CALL DOSELECT RET NZ ; IN A,(BITDAT) SET 3,A ;select S/D RES 5,A ;select side 0 OUT (BITDAT),A ; CALL TURNON ;CLEAR DISK CONTROLLER AND 10000001B ;Mask statuses RET NZ RESTOR: LD A,RSTCMD ;LOAD B WITH A RESTORE COMMAND CALL CMDOUT AND 10011000B ;Mask off TRK 0 status LD (TRACK),A ;SET TRACK# IN MEM TO ZERO (A is 0) RET ;If any errors trk# is wrong ; ;LOGIN checks the disk density ; LOGIN: CALL HOME RET NZ ;Now log on the disk to see if it is double or single density INC A ;read sector 1 LD (SECTOR),A ; LD HL,(IOPTR) LD (OLDPTR),HL LD HL,LOGBUF ;default dsk buffer LD (IOPTR),HL ; LD A,80H ;Bit 7 set to indicate LOGread LD (RECLEN),A ;Default S/D CALL READ ; LD HL,(OLDPTR) ;Restore previos DMA addr LD (IOPTR),HL RET NZ ;to DSKERR ; LD HL,LOGBUF ;look for 'DS' on the label LD A,'D' CP (HL) JR NE,SINGLED INC HL LD A,'S' CP (HL) JR NE,SINGLED ; If we are here then it is at least double sided INC HL LD A,'D' CP (HL) JR NE,DOUBLES INC HL CP (HL) JR NE,DOUBLES ;Here it must be double sided double density LD A,11B ;128 bytes/sector +D/D + D/S JR SD2 DOUBLES: LD A,10B ;128 bytes/sector +D/S JR SD2 ; Load the sector length/2 for read/writes SINGLED: LD A,00B ;128 bytes/sector SD2: LD (RECLEN),A ;Now we must change the Disk Parameter Blocks LD HL,DPB0 ;The D/D disk parameter block LD BC,XLT0 ;The D/D interleave table BIT 0,A ;Test D/D flag JR NZ,SETPATCH ;If D/D then use those values LD HL,DPB1 ;Else load D/S values LD BC,XLT1 BIT 1,A ;Test D/S flag JR NZ,SETPATCH LD HL,DPB2 ;Else load S/D DPBlk SETPATCH: ;Now have to calculate the patch address to use LD A,(UNIT) OR A ;Is it A:? (DPE0) JR NZ,NEXTPATCH ;No, patch DPE1 LD (PATCH1),BC LD (PATCH2),HL JR DONEPATCH NEXTPATCH: LD (PATCH3),BC LD (PATCH4),HL DONEPATCH: XOR A RET ; ; ; SEEK: PUSH BC ;Save trk # CALL DOSELECT POP BC RET NZ CALL TURNON ;CLEAR DISK CONTROLLER BIT 7,A RET NZ ;EXIT IF DRIVE NOT READY LD A,(TRACK) ;Get current track# OUT TRKREG,A ;to the 1771 LD HL,RECLEN ;Dont do next if D/D BIT 1,(HL) ;is it D/D ? SCF ;clr Carry CCF ;ready to select side 0 JR Z,SK1 ;skip track/side calculation if S/D RR C ;now carry is side#, track is in C ;PA5 is side select SK1: IN A,(BITDAT) ;get side select mask RES 5,A ;default side 0 again LD B,0 ;use B as carry mask RR B ;carry into b7 of B RR B RR B ;now in b5 of B OR A,B ;OR BITDAT with B, the side select mask LD B,A ;save BITDAT (A) to B ;Are we reading a single or double density track? SET 3,B ;set up BITDAT mask for S/D BIT 5,B ;test side bit for side 1 JR NZ,SK2 ;so that trk 0, side 0 is always S/D LD A,C OR A ;On side 0, is the trk # also = 0 ? JR Z,NSKP ;yes, always make trk 0, side 0, S/D SK2: BIT 0,(HL) ;(RECLEN) (Z=S/D) ; SET 3,B ;DD* bit is reset ready for single density JR Z,NSKP ; exit calculation if single density disk RES 3,B ;here it must be DD NSKP: LD A,B ;(get BITDAT mask back to A ready for) OUT (BITDAT),A LD A,(TRACK) ;GET old TRACK# SUB C ;Is new trk the same as the last? RET Z ;Yes,return with 0 in A LD A,C ;get new trk# LD (TRACK),A ; STORE TRACK# FOR SEEK OUT (DATREG),A ;OUTPUT TRACK # TO 1771 LD A,SKCMD ;LOAD A WITH A SEEK COMMAND AND CALL CMDOUT AND 10011000B ;MASK TO READY,SEEK AND CRC ERROR PUSH AF ; ;Now a short delay to allow head to settle IF IS5MHZ LD B,6 ;Loop 6 times at 5MHz ELSE LD B,3 ENDIF ;IS5MHZ LP24: IN A,CTC3 ;Get current CTC3 value LD C,A ;Save it LP23: IN A,CTC3 CP C ;Loop until it has changed JR NZ,LP23 ;The first loop may be a short cycle DJNZ LP24 ;the rest will be a full 5.5 msec at 5MHz ; POP AF RET ;RETURN FINAL SEEK STATUS IN A ; ; ;We wont check if drive ready here, error handler will fix any exceptions ;Famous last words, system lost speed if following call to TURNON was omitted== WRITE: PUSH HL ;HL is required for SELECT errors CALL TURNON ; If disk is write protected prompt now BIT 6,A JP Z,NXT571 ;Z = OK CALL PNEXT ;Print to console DB CR,LF,7,'Disk is write protected',EOT CALL CPMIN ;Get any key to acknowledge message CALL TURNON ;Operator may be asleep NXT571: AND 11000001B POP HL RET NZ ;EXIT IF DRIVE NOT READY or still protected LD B,WRTCMD JR RDWRT READ: PUSH HL CALL TURNON AND 10000001B POP HL RET NZ ;EXIT IF DRIVE NOT READY LD B,RDCMD RDWRT: LD HL,CMDTYP LD (HL),B ;SAVE READ/WRITE COMMAND BYTE INC HL LD (HL),4 ;SET DISK OPERATION RE-TRY COUNT RW1: CALL SETUPNMI ;Are we reading a single or double density track? LD B,80H ;set up B for S/D SK4: ;B=NUMBER OF BYTES/SECTOR LD C,DATREG ;C=1771 DATA REGISTER PORT# LD A,(SECTOR) ;GET SECTOR NUMBER OUT (SECREG),A ;OUTPUT SECTOR# TO 1771 CALL FORCE ;ISSUE A FORCE INTERRUPT COMMAND BIT 5,A ; TO TEST CURRENT HEAD LOAD STATUS LD A,(CMDTYP) ;GET READ OR WRITE COMMAND BYTE JR NZ,RW2 ;JUMP IF HEAD IS ALREADY LOADED OR HLOAD ; ELSE MERGE IN HLD BIT RW2: LD HL,RECLEN BIT 7,(HL) LD HL,(IOPTR) ;Set up DMA pointer OUT CMDREG,A ;START the 1771 reading/writing JP NZ,LOGLOOP ;Reclen test above BIT 5,A ;TEST IF COMMAND IS A READ OR WRITE JR NZ,WLOOP ; AND JUMP TO THE CORRECT LOOP RLOOP: HALT INI JP NZ,RLOOP RW6: CALL IRQ2 AND 10011100B ;MASK OFF TO READY, NOT FOUND, CRC JR RW3 ; AND LOST DATA STATUS BITS ; LOGLOOP: LD B,7AH ;Bytes to discard LPZY2: HALT IN A,(C) DJNZ LPZY2 ;Speed not important as only S/D LD B,6 LPZY3: HALT ;Now collect the data bytes INI JP NZ,LPZY3 JR RW6 ; WLOOP: HALT OUTI JP NZ,WLOOP CALL IRQ2 AND 11111100B ;MASK OFF AS ABOVE PLUS WRITE FAULT RW3: RET Z ;RETURN IF NO DISK I/O ERRORS LD HL,RETRY DEC (HL) ;DECREMENT RE-TRY COUNT AND JR NZ,RW1 ;Execute command again if not=0 OR A RET ;ELSE RETURN 1771 ERROR STATUS ; ; ;Forcing a D1 interrupt here hangs (197X app p3) CMDOUT: OUT CMDREG,A ;Output the command CALL SETUPNMI IRQ2: HALT CALL RESTORENMI IN A,STSREG RET ; FORCE: LD A,FINCMD OUT CMDREG,A LP7: IN A,STSREG BIT 0,A ;Still busy? JR NZ,LP7 RET ; SETUPNMI: DI LD HL,NMIVEC LD D,(HL) LD A,0C9H LD (HL),A RET ; RESTORENMI: LD HL,NMIVEC LD (HL),D EI RET ; TURNON: LD A,30 LD (MOTOR),A ;RE-LOAD THE MOTOR TURN-OFF TIMER ;Just testing the motors status here is no good if ; a new drive has just been selected ; Test to see if new drive is ready here CALL FORCE ; AND 11011001B ;do in main routines BIT 7,A ;Ready? RET Z ; AND EXIT IF STILL TURNED ON IN A,(BITDAT) AND 11111011B ;ELSE RE-ENABLE THE DRIVE SELECTS OUT (BITDAT),A ; AND ACTIVATE THE MOTOR RELAY PUSH BC LD B,0 ;SET READY LOOP MAX TIMEOUT ; TURN2: CALL WAIT ;WAIT 1/93 SECOND AND TEST READY RLA ;Test the ready status bit JR NC,TURN3 ;EXIT LOOP IF DRIVE READY DJNZ TURN2 ; ELSE TRY AGAIN UP TO 256 TIMES TURN3: LD B,4 TURN4: CALL WAIT ;GIVE ABOUT 1/20 SECOND MORE DELAY DJNZ TURN4 ;to ensure no CRC errors ; CALL FORCE ;Done in WAIT ; AND 11011001B ;do in main routines POP BC RET ; ; WAIT: IN A,(CTC3) ;GET CURRENT CTC3 COUNT VALUE LD C,A WAIT2: IN A,(CTC3) CP C ;TEST IF CTC3 HAS CHANGED BY 1 COUNT JR Z,WAIT2 ; AND LOOP UNTIL IT CHANGES JR FORCE ; THEN GO TEST DRIVE READY STATUS ; ; ;**************************************** ;* * ;* DISK I/O BUFFER, PARAMETER TABLES * ;* * ;**************************************** ; DPBASE EQU $ ;BASE OF DISK PARAMETER BLOCKS ;Use two Single Sided Single Density Blocks ;Which can be patched as required DPE0: PATCH1: DW XLT1,0000H ;TRANSLATE TABLE DW 0000H,0000H ;SCRATCH AREA DW DIRBUF PATCH2: DW DPB2 ;DIR BUFF,PARM BLOCK DW CSV0,ALV0 ;CHECK, ALLOC VECTORS (Use largest ones always) DPE1: PATCH3: DW XLT1,0000H ;TRANSLATE TABLE DW 0000H,0000H ;SCRATCH AREA DW DIRBUF PATCH4: DW DPB2 ;DIR BUFF,PARM BLOCK DW CSV1,ALV1 ;CHECK, ALLOC VECTORS ; ; DOUBLE DENSITY DOUBLE SIDED ; DISKDEF 0,1,34H,4,4096,(77-2)*2*52/32,192,192,2 DPB0 EQU $ ;DISK PARM BLOCK DW 52 ;SEC PER TRACK DB 5 ;BLOCK SHIFT DB 31 ;BLOCK MASK DB 0 ;EXTNT MASK DW 242 ;DISK SIZE-1 DW 191 ;DIRECTORY MAX DB 192 ;ALLOC0 DB 0 ;ALLOC1 DW 48 ;CHECK SIZE DW 2 ;OFFSET ; ;SINGLE DENSITY DOUBLE SIDED ; DISKDEF 1,1,1AH,6,2048,(77-2)*2*1AH/16,128,128,2 DPB1 EQU $ ;DISK PARM BLOCK DW 26 ;SEC PER TRACK DB 4 ;BLOCK SHIFT DB 15 ;BLOCK MASK DB 0 ;EXTNT MASK DW 242 ;DISK SIZE-1 DW 127 ;DIRECTORY MAX DB 192 ;ALLOC0 DB 0 ;ALLOC1 DW 32 ;CHECK SIZE DW 2 ;OFFSET ; ;SINGLE DENSITY SINGLE SIDED ; DISKDEF 1,1,1AH,6,1024,(77-2)*1AH/8,64,64,2 DPB2 EQU $ ;DISK PARM BLOCK DW 26 ;SEC PER TRACK DB 3 ;BLOCK SHIFT DB 7 ;BLOCK MASK DB 0 ;EXTNT MASK DW 242 ;DISK SIZE-1 DW 63 ;DIRECTORY MAX DB 192 ;ALLOC0 DB 0 ;ALLOC1 DW 16 ;CHECK SIZE DW 2 ;OFFSET ; XLT0: ;TRANSLATE TABLE (Double Density) DB 1,5,9,13,17,21,25,29,33,37,41,45,49,2,6,10,14,18,22,26,30,34 DB 38,42,46,50,3,7,11,15,19,23,27,31,35,39,43,47,51,4,8,12,16,20 DB 24,28,32,36,40,44,48,52 ; XLT1: ;TRANSLATE TABLE (IBM) DB 1,7,13,19,25,5,11,17,23,3,9,15,21,2,8,14,20,26,6,12,18,24,4,10,16,22 ; DIRBUF: DS 128 ;DIRECTORY ACCESS BUFFER ; ALLOCATION.VECTORS: ;Must be zeroed on Warm Boot ALV0: DS 31 ;Double Sided, Double Density CSV0: DS 48 ; ALV1: DS 31 CSV1: DS 48 VECTORS.LENTH: EQU $-ALLOCATION.VECTORS ; ; ;These are smaller, and not required ;ALV: DS 31 ;Double Sided Single Density ;CSV: DS 16 ;ALV: DS 31 ;Single Sided Single Density same as DSSD ;CSV: DS 16 ; ; LOGBUF: DS 6 ;For logging in disks ; SAVE.DRIVE: DS 1 ; END.ROM: EQU $ !Ignore error ; ; ;******************************************************** ;* * ;* STORAGE ALLOCATION FOR 256 BYTE FIXED RAM * ;* * ;******************************************************** ; ORG 0FF00H ;Top page of RAM : MEM: EQU $ ; VECTAB EQU $ ;INTERRUPT VECTOR TABLE STARTS HERE SIOVEC: DEFS 16 ;SPACE FOR 8 VECTORS FOR SIO CTCVEC: DEFS 8 ;SPACE FOR 4 VECTORS FOR CTC SYSVEC: DEFS 4 ;SPACE FOR 2 VECTORS FOR SYSTEM PIO GENVEC: DEFS 4 ;SPACE FOR 2 VECTORS FOR GENERAL PIO ; ; ; ; DISK I/O DRIVER VARIABLES ; Be careful not to straddle a Page Boundary with these UNIT: DEFS 1 ;CURRENTLY SELECTED DISK# TRKTAB: DEFS 2 ;2 DRIVE HEAD POSITION TABLE TRKTAB.LENGTH: EQU $-TRKTAB RECLENTAB: DS 2 ;2 Drive record length table MOTOR: DEFS 1 ;DRIVE MOTOR TURN-OFF TIMER RECLEN: DEFS 1 ;Current disk 'Record Length' TRACK: DEFS 1 SECTOR: DEFS 1 CMDTYP: DEFS 1 ;COMMAND BYTE FOR READS/WRITES RETRY: DEFS 1 ;DISK OPERATION RE-TRY COUNT IOPTR: DEFS 2 ;DISK I/O BUFFER POINTER OLDPTR: DS 2 ;A scratch location used by RESTORE RWFLAG: DS 1 ;Discriminates reads/writes ; ; ; ; KEYBOARD DATA INPUT FIFO VARIABLES FIFO: DEFS 16 ;CONSOLE INPUT FIFO FIFCNT: DEFS 1 ;FIFO DATA COUNTER FIFIN: DEFS 1 ;FIFI INPUT POINTER FIFOUT: DEFS 1 ;FIFO OUTPUT POINTER ; ; ; STACK POINTER SAVE AND LOCAL STACK FOR INTERRUPT ROUTINES TMPSTK: EQU $ ;Internal stack for all routines DEFS 16 ;LOCAL STACK FOR INTERRUPTS SPSAVE: DS 2 ;Save stack pointer ; ; ; 'SOFTWARE' VECTORS FOR INTERRUPT SERVICE ROUTINES TIKVEC: DEFS 2 ;ONE SECOND INTERRUPT ROUTINE VECTOR PINVEC: DEFS 2 ;PARALLEL CONSOLE INPUT VECTOR SINVEC: DEFS 2 ;SERIAL CONSOLE INPUT VECTOR ; ; ; CLOCK-TIMER INTERRUPT VARIABLES TIKCNT: DEFS 2 ;BINARY CLOCK TICK COUNTER DAY: DEFS 1 ;CALENDAR DAY MONTH: DEFS 1 ; MONTH YEAR: DEFS 1 ; YEAR HRS: DEFS 1 ;CLOCK HOURS REGISTER MINS: DEFS 1 ; MINUTES RETISTER SECS: DEFS 1 ; SECONDS REGISTER ; ; CRT OUTPUT DRIVER VARIABLES CURSOR: DEFS 2 ;CURSOR POINTER CHRSAV: DEFS 1 ;CHARACTER OVERLAYED BY CURSOR CSRCHR: DEFS 1 ;CHARACTER USED FOR A CURSOR BASE: DEFS 1 ;CURRENT CONTENTS OF SCROLL REGISTER LEADIN: DEFS 1 ;STATE OF LEAD-IN SEQUENCE HANDLER ; ; ; NULL PAD COUNT FOR SERIAL OUTPUT DELAY ;NULLS: DEFS 1 ;# OF NULLS SENT AFTER CONTROL CHARS. ; ; ; CONSOLE MONITOR PROGRAM VARIABLES PARAM1: DEFS 2 ;STORAGE FOR NUMBERS READ PARAM2: DEFS 2 ; FROM LINE INPUT BUFFER PARAM3: DEFS 2 ; BY 'PARAMS' SUBROUTINE PARAM4: DEFS 2 ESCFLG: DEFS 1 ;CONSOLE ESCAPE FLAG COFLAG: DEFS 1 ;CONSOLE OUTPUT TOGGLE LAST: DEFS 2 ;LAST ADDRESS USED BY 'MEMDMP' HLSAV: DS 2 ;Storage of values at entry DESAV: DS 2 BCSAV: DS 2 SPSAV: DS 2 ROUTINE.ADDRESS: DS 2 ;Where to restart on errors OFFSET: DS 1 ;the offset (function #) LINBUF: DEFS 64 ;CONSOLE LINE INPUT BUFFER ; STACK: EQU 0FFFFH ;Top of memory ;STACKLENGTH: EQU STACK-[LINBUF+64] !Ignore error ; END