;************************************************* ;* * ;* Driver modules for Big Board Computer * ;* running InfoSoft IOS 2.34 * ;* * ;* Written by Trevor Marshall * ;* May-June 1981 * ;* * ;* (C) Copyright 1981 by Trevor Marshall * ;* * ;* Disk CACHE BUFFER version * ;* * ;* Serial or Parallel Printer version * ;* Honeywell or Clare keyboards * ;* * ;************************************************* ; NAME MYDRV ; ENTRY MYINIT ENTRY MYCLK ENTRY MYCON ENTRY MYTERM ENTRY MYLIST ENTRY MYPUN ENTRY MYRDR ENTRY MYBYE ENTRY MYDUB ENTRY LOGBUF ; EXT CONPRY,WBOOT ;the IOS link to CONPROC EXT CSDTE,UEJECT,DELAY EXT SYABT,URREG,SPLLST ; TRUE EQU -1 FALSE EQU 0 ; SERIAL EQU FALSE ;This version for serial printer PARALLEL EQU NOT SERIAL ;or for parallel printer IS5MHZ EQU TRUE ;5MHz or 2.5 MHz clock? HONEYWELL EQU TRUE ;Honeywell SD103 kybd csr codes CLARE EQU NOT HONEYWELL ;CLARE C70/MGP keyboard MULTI.USER EQU TRUE ;Do we have 192K of RAM? NO.CACHE EQU FALSE ;Are we only using 64K RAM? ; ; MYCON: DW MYCINIT,MYCSTAT,MYCIND,MYCRDY,MYCOUTD MYTERM: DW MYCSET,MYCIN,MYCOUT MYLIST: DW MYLINIT,MYLRDY,MYLOUT MYRDR: DW MYRINIT,MYRSTAT,MYRIN MYPUN: DW MYPINIT,MYPRDY,MYPOUT MYCLK: DW SCLK,RDCLK ; SCLK: RDCLK: RET MYBYE: JP 0 ; ; I/OS CONSOLE DRIVER ; MYCIND: MYCIN: CALL DOCLOSE ;Close any DDBs to be written,then drop through to CONIN: JP KBDIN ;These vectors are modified by the swstash routine MYCINIT: XOR A DEC A ;**************************************????????? LD (DELAY.FLG),A ;Indicate no delay on O/P chars ; MYCRDY: MYRSTAT: MYPRDY: LD A,0FFH MYPINIT: MYPOUT: RET ; MYCOUT: MYCOUTD: CONOUT: JP CRTOUT ;to reflect par or VDU system ; MYCSTAT: LD HL,(URREG) ;Allow DOS to continue spooling CALL SPLLST ;Now check CLOSE timeout LD HL,CLOSE.TICKS LD E,(HL) ;We will check for 3000H calls to CONST INC HL LD D,(HL) LD A,E OR A JR NZ,INC LD A,D CP 30H JR LT,INC PUSH HL CALL DOCLOSE ;If 3000H calls to CONST have elaped then CLOSE POP HL LD DE,0 INC: INC DE LD (HL),D DEC HL LD (HL),E ;Then continue CONST call CONST: JP KBDST ; consoles ; ; ; Set Special Console Command Including Cursor Addressing ; Upon Entry: for cursor addressing : ; E contains cursor row in range 1-24 ; D contains cursor column in the range 1-80 ; for special console command : ; E = 0 ; D contains the special command number ; ; MYCSET: LD A,E ; Check whether it's a special AND A ; or cursor-address command JR Z,CSCOMMD ; Skip to do special command LD A,1BH ; Escape character starts CALL MYCOUT ; cursor-address command LD A,'=' ; Second special character is "=" CALL MYCOUT ; Output it LD A,1FH ; Load A with offset to generate row ADD E ; Add incoming row number to offset CALL MYCOUT ; Output so-created character LD A,1FH ; Load A with offset to generate column ADD D ; Add incoming column number to offset JP MYCOUT ; Output so-created character & return ; ; ; Set special console entry command (part of CSET) ; Upon entry: D contains the special command number ; CSCOMMD: LD A,D ; Gte number of special command CP SC.MAX ; Check for illegal special RET NC ; command and return if so LD HL,SC.TBL ; Point to table of special command values ; ADD L ; Add offset in A to table address in HL LD L,A ; / JR NC,CSC200 ; / INC H ; / CSC200: LD A,(HL) ; Get the command from the table AND A ; Zero means command from table RET Z ; Return if command not implemented JP MYCOUT ; Output the special character ; ; Special command table for ADM-3A terminal ; SC.TBL: DB 1AH ; 0 - Clear screen DB 1EH ; 1 - Home cursor DB 08H ; 2 - Back space DB 0CH ; 3 - Forward space DB 0BH ; 4 - Move cursor up DB 0AH ; 5 - Move cursor down DB 18H ; 6 - Clear to EOL DB 11H ; 7 - Clear to EOS DB 0 ;not implemented ; SC.MAX EQU $-SC.TBL ; Length of table ; IF PARALLEL MYLRDY: IN A,PIOBDAT ;Is printer busy? CPL AND 2 ;if lo,busy RET Z ;if so ret zero LD A,0FFH MYLINIT: RET MYLOUT: PUSH AF LP1: CALL MYLRDY ;Is printer busy? JR Z,LP1 POP AF OUT PIOADAT,A ;Write the data to port A IN A,PIOBDAT ;Strobe port B RES 7,A OUT PIOBDAT,A SET 7,A OUT PIOBDAT,A RET ENDIF ;Parallel ; IF SERIAL ;Using SIO channnel A ; MYLRDY: IN A,6 AND 4 JR NZ,CHECKCTS ;THe UART is ready, is the printer? XOR A ;ready for MYLINIT: RET ;not ready rtn ; Note that the SIO automatically has CTS status affects ; the TBE bit, so this is not necessary CHECKCTS ;IN A,6 ; AND 20H ;ckech Clear To Send ; LD A,0 ; RET Z ;A=0 if not ready LD A,0FFH ;else A=0FFH RET MYLOUT: PUSH AF L11: CALL MYLRDY OR A JR Z,L11 POP AF OUT 4,A ;Output 8 bits RET ENDIF ;SERIAL ; ; MYRIN: LD A,1AH ; MYRINIT: RET ; ; MYINIT: DI ;Zero the monitor RAM area first LD HL,MEM LD DE,MEM+1 LD BC,MLNGTH-1 LD (HL),0 LDIR ; Unless we switch to internal stack the bank switch fouls up, as the DOS hands ; us a stack at 100H initially, which is bank switched out LD (SPSAV2),SP LD SP,STACK ; ; INITIALIZE THE Z-80 FOR INTERRUPT MODE #2 LD HL,VECTAB 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) RES 7,A ;Deselect CRT bank 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 ; 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 ; ; Because LINKA cannot relocate DBs we will have to program the vectors ; for interrupts seperately ; ;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,83H OUT (KBDCTL),A ;ENABLE INTERRUPTS FROM KBD PIO EI ; ; LD C,SIOCPB ; LD A,2 ;Select read register 2 for SIO test ; OUT (C),A ; IN A,(C) ;TEST SIO READ REGISTER 2 TO see ; AND 0FH ;Allow for vectors on any 10H boundaries ; CP 6H ; IF THE SIO IS INSTALLED ; JR NZ,PARAL ;SKIP CONFIGURATION TEST IF NO SIO ; LD B,10H ;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 ; ;PARAL: CALL KBDIN ;clear char SIGNON: ; IF MULTI.USER LD A,58H ;now select and home A: ELSE LD A,48H ;now select and home A: ENDIF ;MULTI.USER OUT (BITDAT),A CALL HOME ;We need to create DDBs for both drives LD HL,UNIT LD (HL),0 PUSH HL CALL MAKE.DDB POP HL INC (HL) ;Now drive B PUSH HL CALL MAKE.DDB POP HL LD (HL),0FFH ; ;now to the DOS LD SP,(SPSAV2) ;Retrieve entry stack RET ;** Go execute IOS ** ; ; 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 ; LD HL,RATES-1 ; ; IF IS5MHZ ; RRA ; /2 compensates for 5 MHz clock ; ENDIF ;IS5MHZ ; ;BAUD3: INC HL ;INDEX INTO BAUD RATE TABLE ; RLA ; USING COUNT DERIVED 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 ; LD HL,SIOOUT ; LD DE,SIOIN ;Also SIO input ; LD BC,SIOST ; CALL SWSTSH ;And modify vectors for VDU ; JR SIGNON ; ; ; BAUD RATE CONSTANTS FOR COM 8116 BAUD RATE GENERATOR ; ;RATES: DEFB 5H ; 300 BAUD ; DEFB 6H ; 600 BAUD ; DEFB 7H ; 1200 BAUD ; DEFB 0AH ; 2400 BAUD ; DEFB 0CH ; 4800 BAUD ; DEFB 0EH ; 9600 BAUD ; DEFB 0FH ; 19200 BAUD ; DEFB 0FH ; 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 ;Note that channel A vectors are not programmed (see Leventhal) ; DEFB 4 ; DEFW SIOVEC+4 ; DEFW SIOINT ;SIO RECIEVE INTERRUPT VECTOR ; DEFW SIOERR ;SIO PARITY, OVERRUN & FRAMING ERROR ; ; ; INITIALIZE DISK I/O DRIVER VARIABLES ; DEFB 6 DEFW UNIT DEFB 0 ;FLAG drive A as selected DEFB 0,0 ;CLEAR HEAD POSITION TABLE DEFB 0,0 ;Both default single density DEFB 30 ;SET MOTOR TURN-OFF TIMER ; ; 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 ; ; PIOADAT: EQU 8 PIOACTL: EQU 9 PIOBDAT: EQU 0AH PIOBCTL: EQU 0BH DB 3,PIOACTL DB 3H ;Disable interrupts DB 0CFH ;Put in BIT mode DB 0 ;All bits outputs ; DB 3,PIOBCTL DB 3H ;Disable interrupts DB 0CFH ;Put in BIT mode DB 6H ;bits 1,2 INPUTS ; DB 1,PIOBDAT DB 80H ;Bell off ; 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 ; ; DEFB 3,BITCTL ; DEFB 0CFH ;PUT SYSTEM PIO IN BIT MODE ; DEFB 0 ;All bits outputs ; DEFB 40H ;DISABLE INTERRUPTS ; ; DEFB 1,BITDAT ; DEFB 08H ;DE-SELECT ROMS, ENABLE DRIVE 0 ; ; DD* line high ; ; SIDE 2 line low (=side 1) ; DEFB 2,KBDCTL DEFB 4FH ;PUT KEYBOARD PORT IN INPUT MODE DEFB (SYSVEC+2)&0FFH ;LOAD KEYBOARD INTERRUPT VECTOR ; ; ; 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&0FFH ;BASE INTERRUPT VECTOR FOR CTC ; ; DEFB 2,CTC2 ; DEFB 00100111B ;PUT CTC2 IN TIMER MODE ; DEFB 105 ;CTC2 PERIOD=105*256*200 nsecs (5MHz) ; ; DEFB 2,CTC3 ; DEFB 11000111B ;PUT CTC3 IN COUNTER MODE ; DEFB 93 ;CTC3 PERIOD=999936/2 MICROSECONDS ; ; ;INITIALIZE SIO CHANEL B FOR ASYNCHRONOUS SERIAL ; INTERFACE TO TERMINAL ; SIO CHANNEL A is used for a serial printer ; 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 ; ; 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 00000100B ;STATUS AFFECTS VECTOR, NO INTERRUPTS ; DEFB 3 ;SELECT REGISTER #3 ; DEFB 11000001B ;8 BITS/RX CHARACTER ; DEFB 5 ;SELECT REGISTER #5 ; DEFB 11101010B ;7 BITS/TX CHARACTER, ASSERT DTR ; DEFB 2 ;SELECT REGISTER #2 ; DEFB SIOVEC&0FFH ;LOAD INTERRUPT VECTOR BASE ; ;ALways initialize channel A to 1200 no ints, PAR: or SER: DB 1,BAUDA DB 0111B ;1200 Baud printer default ; DB 8,SIOCPA DB 1 DB 0 ;no interrupts DB 4 ;register 4 DB 4CH ;01001100B ;1.5 stop bits, no ints DB 3 DB 0E1H ;11100001B ;8 bits/char, auto enables DB 5 DB 0EAH ;11101010B ;8 bits/cahr ; DEFB -1 ;END-OF-TABLE ; ; INIT DONE ; ; ; ; -- SWITCH CONSOLE DEVICE -- ;SWSTSH: ;Store the Serial/Parallel vectors ;SWIT2: LD (CONOUT+1),HL ;STORE NEW CONSOLE OUTPUT ADDRESS ; LD (CONIN+1),DE ; LD (CONST+1),BC ; RET ; ; 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 ; ;Needed to output copyright msg PNEXT: EX (SP),HL CALL PMSG EX (SP),HL RET ; PMSG: LD A,(HL) INC HL CP EOT RET Z CALL CONOUT JR PMSG ; ; ;ASCHEX: SUB '0' ; RET C ; CP 10 ; CCF ; RET NC ; SUB 7 ; CP 10 ; RET C ; CP 16 ; CCF ; RET ; ; PUT4HEX: LD A,H CALL PUT2HEX LD A,L PUT2HEX: PUSH AF RRA RRA RRA RRA CALL PUTNIB POP AF PUTNIB: AND 00001111B ADD A,90H DAA ADC A,40H DAA JP CONOUT ; ; ;******************************************************** ;* * ;* INTERRUPT SERVICE ROUTINES FOR KEYBOARD * ;* INPUT AND REAL-TIME CLOCK FUNCTIONS * ;* * ;******************************************************** ; ; ; KBDST: LD A,(FIFCNT) ;GET INPUT FIFO BYTECOUNT OR A ;TEST IF EQUAL ZERO RET Z ;EXIT WITH A=0 IF QUEUE IS EMPTY ;To implement Type Ahead now check to see if DOS has one char queued yet PUSH HL PUSH DE LD HL,(URREG) LD DE,28 ;URCHR offset in URREG ADD HL,DE LD A,(HL) OR A JR Z,RET.RDY ;If there is no URCHR then OK to send ready ;Here there is a URCHR, so check to see whether to overwrite ; will only pass thru CTL-S,CTL-C,CTL-E,CTL-P and CALL REMOVE PUSH AF CALL REPLACE ;We only want to look at it, not ZAP it POP AF ;Use <^S>,<^C> and sequence to abort batch ; CP 'S'-40H ; JR EQ,RET.RDY ; CP 'C'-40H ; JR EQ,RET.RDY ; CP 'E'-40H ; JR EQ,RET.RDY ; CP 'P'-40H ; JR EQ,RET.RDY CP 0DH JR EQ,NR ;Don't pass CR CP 08H JR EQ,NR ;Or BS CP 20H JR LT,RET.RDY ;All other control chars to go thru NR: XOR A ;else not ready JR RET.NR RET.RDY: LD A,255 RET.NR: POP DE POP HL OR A ;Set Z flag RET ;ELSE SET A=255 TO INDICATE DATA READY ; KBDIN: LD A,(FIFCNT) OR A JR NZ,KBDRDY ;LOOP UNTIL KEYBOARD INPUT READY PUSH HL PUSH DE PUSH BC LD HL,(URREG) ;But do it so as to keep spooler active CALL SPLLST ;while wasting minimal time POP BC POP DE POP HL JR KBDIN KBDRDY: PUSH HL CALL REMOVE ;GET CHARACTER FROM INPUT QUEUE POP HL IF HONEYWELL BIT 7,A ;If B7 set it must be KYBD char code RET Z ENDIF ;HONEYWELL ; We will process all keys for special codes if CLARE is in use ; 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 ;Similar proceedure for CLARE kybd 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 IF HONEYWELL 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 STASH: LD C,A CP 0E7H ;Honeywell Keyboard's CLEAR SCREEN key JR NZ,NXT35 CALL CLRSCN ;Clear the screen IN A,(BITDAT) RES 7,A OUT (BITDAT),A RET ENDIF ;HONEYWELL IF CLARE TBL1: DB 0BH ;Up line DB 89H ;right char ; DB 08H ;left char ;handled without ; DB 8AH ;down line ;translation DB 0FFH ;dummy TBL1.LENGTH: EQU $-TBL1 DB 0 TBL2: DB 05H DB 04H DB 0 STASH: AND 7FH ;delete parity LD C,A ENDIF ;CLARE ; NXT35: IF HONEYWELL CP 85H ;F1 key ENDIF ;HONEYWELL IF CLARE CP 0AAH ENDIF ;CLARE JR Z,NXT135 ;and toggle delay flag ; ;To ensure safe type ahead we must check to see if a ^S,^P,^C,^E are present ; We will in fact pass any control char other than or CP 0DH JR Z,OK CP 08H JR Z,OK CP 20H JR GE,OK ;Else we want to drop the rest of the buffer as ctl char takes priority LD A,1 LD (FIFCNT),A LD (FIFIN),A LD (FIFOUT),A JR ST56 ; OK: LD HL,FIFCNT ;BUMP INPUT FIFO CHARACTER COUNT LD A,(HL) INC A CP 64 ;**********************************-----------**** RET NC ;EXIT NOW IF FIFO IS FULL LD (HL),A ; ELSE INCREMENT FIFO COUNT ST56: LD HL,FIFIN ;POINT HL TO FIFO INPUT OFFSET CALL INDEX LD (HL),C ;STORE CHARACTER IN FIFO @ HL RET ; NXT135: LD A,(DELAY.FLG) CPL ;toggle delay flag LD (DELAY.FLG),A RET ; REMOVE: LD HL,FIFCNT DEC (HL) LD HL,FIFOUT ;POINT HL TO FIFO OUTPUT OFFSET INDEX: LD A,(HL) INC A AND 3FH ;INCREMENT FIFO POINTER LD (HL),A ; MODULO 64 AND REPLACE***********---------**** LD HL,BIGFIFO ;*******************************--------**** ADD L ;INDEX INTO FIFO BY OFFSET IN A LD L,A LD A,(HL) RET ; REPLACE: PUSH HL ;For CSPIM LD HL,FIFCNT INC (HL) LD HL,FIFOUT INDEX2: LD A,(HL) DEC A AND 3FH ;***************-------************* LD (HL),A POP HL 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 ; CALL DOCLOSE ;Not enough stack for this here MOTORS.OFF: IN A,(BITDAT) OR 04H ;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 ; 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 ; 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 -- ; ;SIOINT: ; ; LD (SPSAVE),SP ;SAVE USER STACK POINTER AND ; LD SP,TMPSTK ; SWITCH TO LOCAL STACK ; PUSH HL ; PUSH DE ; PUSH BC ; PUSH AF ;SAVE MACHINE STATE ; IN A,(SIODPB) ;READ SIO DATA INPUT PORT ; AND 7FH ; LD HL,(SINVEC) ;GET SERIAL INPUT ROUTINE VECTOR ; ; 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 ; CALLHL: JP (HL) ; ; ; ; -- RX ERROR INTERRUPT SERVICE ROUTINE FOR SIO -- ; ; ARRIVE HERE IF RECIEVE INTERRUPT FROM FRAMING, OVERRUN ; AND PARITY ERRORS. (PARITY CAN BE DISABLED) ; ;SIOERR: ; ; LD (SPSAVE),SP ;SAVE USER STACK POINTER AND ; LD SP,TMPSTK ; 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) ; EI ; ; ; RETI ; ; ; ; POLLED MODE I/O ROUTINES FOR SIO CHANEL B ; ;SIOST: IN A,(SIOCPB) ;GET SIO STATUS REGISTER ; AND 1 ; 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,30H ;RESET STATUS BITS IN SIO FOR ; OUT (SIOCPB),A ; PARITY/OVERRUN/FRAMING ERRORS, ; IN A,(SIODPB) ; THEN GET THE INPUT CHARACTER ; AND 7FH ; RET ; ; ;SIOOUT: ;SIOXMT: PUSH AF ;SIOX1: IN A,(SIOCPB) ; AND 4 ;TEST TBE STATUS BIT ; JR Z,SIOX1 ; POP AF ; OUT (SIODPB),A ;OUTPUT DATA TO SIO ; RET ; ; ; ;************************************************ ;* * ;* MEMORY-MAPPED CRT OUTPUT DRIVER * ;* * ;************************************************ ; ; CRTMEM EQU 3000H ;Start of 3K CRT memory space CRTBAS EQU 30H ;STARTING PAGE# OF 3K CRT SPACE CRTTOP EQU 3CH ;ENDING PAGE# OF CRT SPACE ; ; CRTOUT: PUSH AF PUSH HL PUSH DE PUSH BC ; RES 7,A LD C,A ; ;See if DELAY flag has been set LD A,(DELAY.FLG) OR A JR Z,DI1 LD A,4 ;4 msec delay to allow easier typing, etc CALL DELAY ;IN IOS DI1: DI ;KEEP THE WOLVES AWAY FOR A WHILE LD (SPSAVE),SP LD SP,TMPSTK ;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 0FH ;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 BIT 7,A ;Is the MSBit set (Graphics char) JR Z,DISPLA ;No, skip translation RES 7,A ;Graphics, select control char DISPLA: LD (HL),C ; ELSE STORE DISPLAYABLE CHARACTER INC HL ; AND ADVANCE POINTER TO NEXT COLUMN LD A,L AND 7FH ;EXTRACT COLUMN# FROM HL CP 80 RET C ;EXIT IF NOT PAST COLUMN 79 CALL RETUR ; ELSE DO AUTOMATIC CARRIAGE RETURN CALL LFEED ; AND LINEFEED RET ; ; ; CONTRL: PUSH HL LD HL,CTLTAB ;SEARCH FOR CONTROL CHARACTER LD BC,CTLSIZ ;HANDLING SUBROUTINE IN TABLE CALL SEARCH POP HL RET NZ ;EXIT IF NOT IMPLEMENTED PUSH BC RET ;DO SNEAKY JUMP TO PRESERVE REGISTERS 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 ; CTLTAB: DEFB 1FH DEFB 1EH DEFB 1BH DEFB 1AH DEFB 18H DEFB 11H DEFB 0DH DEFB 0CH DEFB 0BH DEFB 0AH DEFB 09H DEFB 08H DEFB 07H CTLSIZ: EQU $-CTLTAB 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 RETUR ;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 7FH RET Z ;ABORT IF IN LEFTMOST COLUMN DEC HL ;BACK UP CURSOR POINTER RET ; ; FORSPC: LD A,L ;CHECK FOR RIGHTMOST COLUNM AND 7FH 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 38H ; PREVIOUS TAB POSITION ADD E CP 80 ;EXIT IF NEXT TAB COLUMN WOULD RET NC ; BE PAST THE RIGHT MARGIN LD A,L AND 0F8H ;ELSE INCREMENT THE CURSOR LD L,A ; POINTER FOR REAL ADD HL,DE RET ; ; Bell is on BIGBOARD port bit 4 BELL: IF NOT MULTI.USER ;Don't use BELL if BANK 3 available IN A,(BITDAT) SET 4,A ;TOGGLE BIT 5 OF SYSTEM PIO TO OUT (BITDAT),A ;TRIGGER BELL HARDWARE TO SOUND LD A,150 ;# millisecs CALL DELAY ;in IOS IN A,(BITDAT) ;faster than push/pop RES 4,A OUT (BITDAT),A ELSE IN A,(PIOBDAT) SET 6,A OUT (PIOBDAT),A LD A,800 ;***************************** CALL DELAY IN A,(PIOBDAT) RES 6,A OUT (PIOBDAT),A ENDIF ;NOT MULTI.USER ; RET ; ; RETUR: LD A,L AND 80H 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,3072 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 7FH ;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 1FH 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 1FH 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 1FH 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 80H ;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 C ; OF ESC,'=',ROW,COL SEQUENCE SUB ' '-1 SETR2: SUB 24 JR NC,SETR2 ;MAKE SURE ROW# IS BETWEEN 0 AND 23 ADD 24 OR 60H ;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 80 OR L ;MERGE IN COL# WITH L LD L,A RET M4TST: CALL DISPLA ;DISPLAY THE CONTROL CHARACTER RET ; PASSED IN C ; ; \\\\\\\\\\\\\\\\ //////////////// ; >>>>>>>>>>>>>>>> F A S T <<<<<<<<<<<<<<<< ; //////////////// M O D U L E \\\\\\\\\\\\\\\\ ; ; TITLE CP/M SPEED-UP BY DISK I/O BUFFERING ; AUTHOR Robert A. Van Valzah 12/25/78 ; Rewritten for IOS on Big Board by Trevor Marshall ; SECLEN EQU 80H ;Default LENGTH OF A SECTOR IN BYTES DIRTRK EQU 2 ;Default TRACK WHICH HOLDS DIRECTORY ; SENTTOK EQU 1 ;TOKEN SHOWING DRIVE & TRACK INFO SENT UPDTTOK EQU 1 ;TOKEN SHOWING SECTOR GETS WRITTEN ONLNTOK EQU 3 ;TOKEN SHOWING THAT A DRIVE IS 'ON LINE' ; ; ENTRY CDDB ;For debugging CDDB: EQU $ CREATE.DDB: ; CALL BANK2 IN A,(BITDAT) RES 4,A OUT (BITDAT),A LD (SPSAV3),SP LD SP,0BF00H LD (HL),B ;Store track# ; ; CALL PNEXT ; db 'CREATING DDB size ',EOT ; LD A,(POKE7+1) ; CALL PUT2HEX ; INC HL LD A,(UNIT) ;Get currently logged drive LD (HL),A ;put drive # LD B,1 ;B holds the sector# LP102: ;Number the sectors sequentially INC HL LD (HL),B ;now sector # XOR A ;update token=0 INC HL LD (HL),A LD DE,81H ;data length + BIOS return code ADD HL,DE ;to point at next sector info INC B LD A,B POKE7: CP 53 ;Maxsectors+1 JR NZ,LP102 INC HL LD (HL),0 ;End of DDB marker ; JP RETURN IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD A,E LD SP,(SPSAV3) RET ; ; FAST SET DISK DRIVE, EXTERNALLY REFERENCED ROUTINE FSETDSK: ; ; CALL PNEXT ; DB 'FsetDSK',EOT ; ; CALL BANK2 ;For RDDDB below IN A,(BITDAT) ;Exit, after reselecting main memory RES 4,A OUT (BITDAT),A LD (SPSAV3),SP LD SP,0BF00H ;in baNk2 ; LD E,C ;SAVE DRIVE FOR ON LINE CODE LD A,C ;GET REQUESTED DRIVE LD (REQDSK),A ;SAVE FOR PASS THRU ADD A ;DOUBLE TO FORM WORD INDEX LD C,A ;FORM INDEX IN REG BC LD B,0 LD HL,DIRBUF ;GET NEW DIR DDB POINTER CALL INDXIND LD (DIRADR),HL ;AND SAVE IT LD HL,RDBUF ;GET NEW READ TRACK DDB POINTER CALL INDXIND LD (RDADR),HL ;AND SAVE IT LD HL,WRBUF ;GET NEW WRITE TRACK DDB POINTER CALL INDXIND LD (WRADR),HL LD C,E ;REG BC IS NOW BYTE INDEX LD HL,ONLINE ;BASE OF 'ON LINE' VECTOR ADD HL,BC ;ADD INDEX TO BASE LD A,(HL) ;IS THIS DRIVE ON LINE? OR A LD A,0 ;no errors on return please JP NZ,RETURN ;YES - DONE WITH SECECTION LD (HL),ONLNTOK ;NO - SET ON LINE FLAG AND . . LD HL,(DIRADR) ;AND READ THE DIRECTORY IF IT IS BUFFERED CALL RDDDB JP RETURN ; ; FAST READ SECTOR ; EXTERNALLY REFERENCED ROUTINE. ; FREAD: ; CALL BANK2 ;Switch in CACHE memory,already done in MYIO LD HL,(RDADR) ;GET ADDRESS OF READ TRACK DDB CALL GETSEC ;TRY TO FIND SECTOR IN READ DDB JP C,RETURN ;Found it, switch back to BANK 0 & return LD HL,(DIRADR) ;TRY TO FIND SECTOR IN DIRECTORY DDB CALL GETSEC JP C,RETURN ;Found it, switch back to BANK 0 & return LD HL,(WRADR) ;TRY TO FIND SECTOR IN WRITE DDB CALL GETWSEC JP C,RETURN ;Found it, switch back to BANK 0 & return LD HL,(RDADR) ;SEE IF THERE IS A READ TRACK BUFFER LD A,H OR L JP Z,RDPASS ;NO - THEN JUST PASS THE READ THRU LD A,(REQTRK) ;YES - SEE IF READING FROM NON-DIRECTORY ;POKE2: CP DIRTRK ;SECTORS OF DIRECTORY TRACK ; JP Z,RDPASS ;YES - JUST LET THAT PASS THRU OR A ;See if we are trying for trk 0 *** Unbuffered please JP Z,RDPASS ;Dont buffer trk#0 ; ;Note: I have destroyed E here LD E,A ;Will save trk# in E to miss flags problems WRRET1: PUSH DE PUSH HL CALL WRDDB POP HL POP DE JR NZ,WRERR1 ;If WRDDB returned an error, process it ERRRE1 LD (HL),E ;Fill new track into read DDB ; LD A,(WRTERRF) ; OR A ; RET NZ ;error RDRET1: PUSH HL ;SAVE READ DDB ADDRESS CALL RDDDB ;AND FILL NEW DDB WITH DATA POP HL ;GET READ DDB ADDRESS BACK JR NZ,RDERR1 ERRRE2 CALL GETSEC ;TRANSFER SECTOR FROM DDB TO CALLER (was JP) JP RETURN ; switch back to BANK 0 & return ; WRERR1: CALL REPORT JR C,ERRRE1 ;Ignore if carry set JR NZ,WRRET1 LD HL,0 ;Fake an abort return PUSH HL JP DOCLOSE ;Otherwise warn of a CLOSE error (and retry to WRDDB) ; RDERR1: CALL REPORT JR C,ERRRE2 ;Ignore if carry set JR NZ,RDRET1 JP EXIT ;Do a CTL-C exit here ; REPORT: LD C,A ;Enter if error status is in A REPORT2: ;Else here if it is in C CALL PNEXT DB CR,LF,'**** Disk error, status ',EOT LD A,C ;status into A CALL PUT2HEX REPORT3: CALL PNEXT DB ',enter <^R>, <^I> or <^A> only.',7,EOT ; CALL CONIN CALL CONIN CP 13H ;>= ^S JR GE,REPORT3 ADD 40H PUSH AF CALL CONOUT ;echo the char ;now terminate the line CALL PNEXT DB CR,LF,EOT POP AF SKP34: CP 'I' SCF ;Set carry in case ignore is true RET Z ;with carry test before zero test CP 'A' ;will default to retry SCF CCF ;clear carry RET ; ; FAST SECTOR WRITE, EXTERNALLY REFERENCED ROUTINE. ; FWRITE: ; CALL BANK2 ;Switch in CACHE memory,already done in MYIO ;Now check drive's write protect status CALL TURNON AND 40H JR Z,NXT672 CALL PNEXT DB '-> Drive is write protected',EOT CALL REPORT3 JR C,NXT671 ;ignore JR NZ,NXT672 ;retry NXT671: CALL PNEXT DB 'Cant ignore',EOT JP EXIT ;abort ; NXT672: LD HL,(WRADR) ;TRY TO PUT SECTOR INTO WRITE BUFFER CALL PUTSEC JP C,RETURN ;It went, switch back to BANK 0 & return LD HL,(DIRADR) ;TRY TO PUT IT INTO DIRECTORY BUFFER CALL PUTSEC JP C,RETURN ;It went, switch back to BANK 0 & return LD HL,(RDADR) ;TRY TO PUT INTO READ BUFFER CALL PUTSEC JP C,RETURN ;It went, switch back to BANK 0 & return LD HL,(WRADR) ;IS THERE A WRITE TRACK BUFFER? LD A,H OR L JP Z,WRPASS ;NO - JUST PASS THIS WRITE THRU LD A,(REQTRK) ;YES - SEE IF THIS IS A NON-DIRECTORY ;POKE3: CP DIRTRK ;SECTOR ON THE DIRECTORY TRACK? ; JP Z,WRPASS ;YES - JUST PASS THAT THRU OR A ;Don't buffer trk#0 JP Z,WRPASS WRRET2: ;No, Dump existing write buffer to disk ; LD C,A ; CALL PNEXT ; DB 'Track ',EOT ; LD A,C ; CALL PUT2HEX ; CALL PNEXT ; DB ', WRDDB= ',EOT ; LD A,H ; CALL PUT2HEX ; LD A,L ; CALL PUT2HEX PUSH HL CALL WRDDB POP HL ; PUSH AF ; CALL PNEXT ; DB 'Returned from WRDDB with error code ',EOT ; POP AF ; PUSH AF ; CALL PUT2HEX ; POP AF JR NZ,WRERR2 ERRRE3 LD A,(REQTRK) LD (HL),A ;AND BUFFER UP REQUESTED TRACK ; LD A,(WRTERRF) ; OR A ; RET NZ CALL PUTSEC ;MOVE SECTOR INTO WRITE BUFFER JP RETURN ; WRERR2: CALL REPORT JR C,ERRRE3 JR NZ,WRRET2 LD HL,0 PUSH HL ; JR DOCLOSE ;warn of a close error ; ; CLOSE ALL OPEN DDB'S. WRITE ANY DATA LEFT IN THEM OUT TO ; DISK ; EXTERNALLY REFERENCED ROUTINE. ; GLOBAL CLOS1 CLOS1: CLOSE: DOCLOSE: ;see above XOR A LD (CLOSE.TICKS),A ;Zero the elapsed ticks since last close LD (CLOSE.TICKS+1),A CLOSE1: ; CALL PNEXT ; DB ' Closing..TOS=',EOT ; EX (SP),HL ; LD C,L ; LD B,H ; EX (SP),HL ; LD HL,0 ; ADD HL,BC ; CALL PUT4HEX ; CALL PNEXT ; DB ' Stack ptr=',EOT ; LD HL,0 ; ADD HL,SP ; CALL PUT4HEX ; ; CALL BANK2 ;Select the CACHE memory IN A,(BITDAT) RES 4,A OUT (BITDAT),A LD (SPSAV3),SP LD SP,0BF00H ;in baNk2 LD C,2*3 ;DDB COUNT LD HL,RDBUF ;START OF DDB ADDRESS TABLES CLOS: ; PUSH HL ; LD HL,(SPSAV3) ; CALL PNEXT ; DB ' Start of close SPSAV3=',EOT ; CALL PUT4HEX ; POP HL ; LD E,(HL) ;GET DDB ADDRESS TO REG DE INC HL LD D,(HL) INC HL ;Pointing at next DDB PUSH HL ;SAVE TABLE ADDRESS PUSH BC EX DE,HL ;DDB ADDRESS TO REG HL ; ; CALL PNEXT ; DB 'Calling WRDDB..',EOT ;See also JR CLOSe1 below ; CALL WRDDB ;CLOSE ONE DDB ; PUSH HL ; LD HL,(SPSAV3) ; CALL PNEXT ; DB ' After WRDDB SPSAV3=',EOT ; CALL PUT4HEX ; POP HL ; PUSH AF ; CALL PNEXT ; DB 'Done WRDDB..',EOT ; POP AF LD A,(WRTERRF) ;ANY WRITE ERRORS IN CLOSING? OR A JR Z,CLOSOK ;NO - KEEP CLOSING CALL PNEXT DB CR,LF,'-> CLOSE ERROR,it may be FATAL to abort or ignore',EOT CALL REPORT3 ;Default to retry JR C,CLOSOK ;on ignore JP Z,EXIT ;on abort POP BC POP HL ;Balance stack JP CLOSE1 ;See also JP NZ below CLOSOK: ; CALL PNEXT ; DB 'Close OK..',EOT POP BC ;GET COUNT POP HL ;GET TABLE ADDRESS DEC C ;DONE ALL DDBS YET? JP NZ,CLOS ;NO - KEEP CLOSING ; CALL PNEXT ; DB 'Done close..',EOT XOR A JP RETURN ;YES - ALL DONE ; ; INDEXED INDIRECT ADDRESSING. RETURNS THE WORD AT THE ; ADDRESS BASE + INDEX ; INTERNALLY REFERENCED ROUTINE. ; Entry at INDEXA gets the table address for a buffer ; INDEXA: LD A,(UNIT) RLCA ;A = A * 2 LD B,0 LD C,A ;and drop through to get table entry INDXIND: ADD HL,BC ;ADD IN INDEX LD A,(HL) ;GET LOW ORDER OF RESULT INC HL LD H,(HL) ;GET HIGH ORDER OF RESULT LD L,A RET ; ; GET A SECTOR FROM A WRITE DDB INTO REQDMA. MATCHES ONLY ; IF UPDATE FLAG IS SET. RETURNS FLAG IF FOUND, INTERNALLY REFERENCED ROUTINE. ; GETWSEC: CALL SRCHDDB ;FIND A SECTOR AND TRACK MATCH? RET NC ;NO - RETURN WITHOUT FLAG INC HL ;YES - SEE IF SECTOR HAS BEEN LD A,(HL) ;WRITTEN (UPDATE FLAG=1)? OR A JR NZ,GS1 ;YES - TRANSFER FROM WRITE DDB CALL RDPASS ;NO - BYPASS BUFFERING SCF ;SET FOUND IT FLAG RET ; ; GET A SECTOR FROM A DDB INTO REQDMA. FLAG SET IF FOUND. ; INTERNALLY REFERENCED ROUTINE. ; GETSEC: CALL SRCHDDB ;DO WE HAVE THE REQUESTED SECTOR? RET NC ;NO - RETURN WITHOUT FLAG INC HL ;YES - POINT TO SECTOR DATA GS1: ;ENTRY FROM GETWSEC (ABOVE) INC HL EX DE,HL ;DATA ADDRESS TO REG DE LD HL,(REQDMA) ;DESTINATION TO REG HL ; CALL XFER ;PERFORM THE TRANSFER ; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER. LD B,SECLEN XF11: ; CALL BANK2 IN A,(BITDAT) RES 4,A OUT (BITDAT),A LD A,(DE) LD C,A ;Save it while ; CALL BANK0 IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD (HL),C ;Could do an error check here INC HL INC DE DJNZ XF11 ; CALL BANK2 ;So good/bad flag is correct IN A,(BITDAT) RES 4,A OUT (BITDAT),A LD A,(DE) ;GET GOOD/BAD FLAG OR A ;set the Z flag SCF ;SET 'FOUND IT' FLAG RET ; ; SEARCH A DDB FOR THE REQUESTED TRACK AND SECTOR ; INTERNALLY REFERENCED ROUTINE. ; SRCHDDB: LD A,H ;SEE IF DDB IS PRESENT AT ALL OR L RET Z ;NO - WE DON'T HAVE IT LD A,(REQTRK) ;YES - SEE IF REQTRK MATCHES DDB TRACK XOR (HL) RET NZ ;NO MATCH - WE DON'T HAVE IT INC HL ;TRACKS MATCH - CHECK SECTORS INC HL LD A,(REQSEC) LD C,A PATCH2: LD DE,SECLEN+3 ;BYTES BETWEEN SECTOR NUMBERS SRCH1: LD A,(HL) ;GET A SECTOR NUMBER FROM DDB OR A ;END OF DDB? RET Z ;YES - RETURN WITHOUT FLAG CP C ;NO - SEE IF REQSEC MATCHES DDB SECTOR SCF ;PREPARE TO RETURN FLAG IF SO RET Z ;THEY MATCH - RETURN FLAG ADD HL,DE ;NO MATCH - ON TO NEXT SECTOR JR SRCH1 ; RDPASS: ; CALL BANK0 ;For unbufferred (trk 0) IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD SP,(SPSAV3) CALL PASSTHRU JR NZ,RDERR3 ERRRE5: JP READ ; RDERR3: CALL REPORT JR C,ERRRE5 JR NZ,RDPASS JP 0 ;Abort system ; ; MOVE A SECTOR FROM MEMORY INTO A DDB ; INTERNALLY REFERENCED ROUTINE. ; PUTSEC: CALL SRCHDDB ;DOES IT GO IN THIS DDB? RET NC ;NO - RETURN WITHOUT FLAG INC HL ;POINT TO DATA FIELD OF DDB LD (HL),UPDTTOK ;SET UPDATE FLAG INC HL EX DE,HL ;SAVE DDB ADDRESS IN REG DE LD HL,(REQDMA) ;GET SOURCE ADDRESS INTO REG HL EX DE,HL ;NOW REG HL=DDB ADDRESS (DEST) AND . . ; CALL XFER ;REG DE=MEMORY (SOURCE) ; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER. LD B,SECLEN XF12: ; CALL BANK0 IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD A,(DE) LD C,A ;Save it while ; CALL BANK2 IN A,(BITDAT) RES 4,A OUT (BITDAT),A LD (HL),C ;Could do an error check here INC HL INC DE DJNZ XF12 ; CALL BANK2 ;So good/bad flag is correct XOR A ;RETURN WITHOUT WRITE ERROR LD (HL),A ;SET GOOD/BAD FLAG TO GOOD SCF ;SET SECTOR PUT FLAG RET ; WRPASS: ; CALL BANK0 IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD SP,(SPSAV3) CALL PASSTHRU JR NZ,WRERR3 ERRRE6 JP WRITE ; WRERR3: CALL REPORT ; JR C,ERRRE6 JR NZ,WRPASS LD HL,0 PUSH HL JP DOCLOSE ;otherwise warn ; PASS ALL DISK I/O PARAMETERS THRU TO BIOS ; INTERNALLY REFERENCED ROUTINE ; PASSTHRU: LD A,(REQDSK) ;PASS REQUESTED DISK THRU LD C,A CALL SELECT RET NZ LD A,(REQTRK) ;PASS REQUESTED TRACK THRU LD C,A ;prepare for SEEK ;Check if it is an illegal sector on TRK #0 OR A JR NZ,NOT.TRK.0 LD A,(REQSEC) ;Is the sector illegal? CP 1BH ; >= 1BH? JR C,NOT.TRK.0 ;C means .LT. 1BH XOR A ;return no errors POP HL ;tricky stack op, return to IOS, not to RDPASS RET ;return if .GE. 1BH on trk 0 NOT.TRK.0: CALL SEEK RET NZ LD A,(REQSEC) ;PASS REQUESTED SECTOR LD (SECTOR),A LD HL,(REQDMA) ;PASS REQUESTED DMA ADDRESS ; XOR A ;Z flag RET ;Leave our DMA addr in HL ; ; READ A DDB FROM DISK. ALL SECTORS ARE FILED IN, AND THEIR ; UPDATE FLAGS ARE RESET. ; INTERNALLY REFERENCED ROUTINE. ; RDDDB: LD A,H ;DO NOTHING FI NO DDB OR L RET Z PUSH HL ;SAVE DDB ADDRESS CALL SENDDT ;SEND DRIVE AND TRACK INFO TO BIOS POP HL ;GET DDB ADDRESS RET NZ INC HL ;POINT TO FIRST SECTOR BYTE INC HL RDDSEC: LD A,(HL) ;GET SECTOR NUMBER OR A ;END OF DDB? RET Z ;YES - ALL DONE LD (SECTOR),A INC HL ;POINT TO UPDATE FLAG LD (HL),0 ;RESET UPDATE FLAG INC HL ;POINT TO DATA FIELD PUSH HL ;SAVE DATA POINTER ;My read requires DMA addr in HL CALL READ ;PERFORM THE READ POP HL ;GET DATA ADDRESS BACK RET NZ PATCH3: LD BC,SECLEN ;BYTES TO GOOD/BAD FLAG ADD HL,BC LD (HL),A ;STORE GOOD/BAD FLAG FROM BIOS IN DDB INC HL JR RDDSEC ;ON TO NEXT SECTOR ; ; WRITE A DDB BACK TO DISK. ONLY SECTORS WITH THE UPDATE FLAG ; SET GET WRITTEN. ; THE DRIVE AND TRACK INFORMATION FROM THE DDB IS SAVED BY ; THIS ROUTINE AND PASSED TO THE BIOS BY RCLDT ONLY IF ; A SECTOR IS TO BE WRITTEN. THIS IS DONE TO BYPASS BIOS ; DELAYS WHEN SWITCHING DRIVES AND TO PREVENT NEEDLESS ; SEEKING TO TRACKS WHICH MIGHT NOT RECIEVE ANY DATA. ; INTERNALLY REFERENCED ROUTINE. ; WRDDB: XOR A ;CLEAR WRITE ERROR FLAG LD (WRTERRF),A LD A,H ;SEE IF A DDB IS PRESENT OR L RET Z ;NO - ALL DONE WRITING LD A,(HL) ;GET TRACK NUMBER INC A ;TRACK = 0FFH (MEANING EMPTY)? RET Z ;YES - NO WRITING TO DO LD (TRKPTR),HL ;NO - SAVE DISK & TRACK POINTER FOR XOR A ;RECALL LATER, AND RESET FLAG LD (SENTFLG),A ;SHOWING THAT THEY HAVEN'T BEEN SENT INC HL ;POINT TO FIRST SECTOR NUMBER INC HL TSTUPDT: LD A,(HL) ;GET SECTOR TO REG A OR A ;END OF DDB? RET Z ;YES - ALL DONE LD C,A ;SAVE SECTOR IN REG C INC HL ;POINT TO UPDATE FLAG LD A,(HL) ;GET UPDATE FLAG OR A ;DOES THIS SECTOR NEED UPDATING ON DISK? ; PUSH AF ; CALL PNEXT ; DB ',',EOT ; POP AF ; PUSH AF ; CALL PUT2HEX ; POP AF CALL NZ,WRSEC ;YES - WRITE IT BACK ; LD A,(WRTERRF) ;Errors handled in WRSEC ; OR A ; RET NZ PATCH4: LD DE,SECLEN+2 ;BYTES TO NEXT SECTOR NUMBER ADD HL,DE JR TSTUPDT ;CHECK THE NEXT UPDATE FLAG ; ; WRITE A SECTOR FROM A DDB BACK OUT TO DISK. ; INTERNALLY REFERENCED ROUTINE. ; WRSEC: ;Defer reset until errors notified ; LD (HL),0 ;RESET UPDATE FLAG PUSH HL ;SAVE UPDATE FLAG POINTER ; PUSH HL ; LD HL,(SPSAV3) ; CALL PNEXT ; DB ' In WRSEC SPSAV3=',EOT ; CALL PUT4HEX ; POP HL WR45: LD A,C ;my set sector LD (SECTOR),A CALL RCLDT ;PASS DRIVE AND TRACK TO BIOS OR A ;because of RCLDT return JR NZ,NXT341 ;to set error flag POP HL ;GET UPDATE FLAG POINTER PUSH HL ;AND SAVE AGAIN INC HL ;POINT TO DATA ;Our write requires DMA addr in HL ; ; CALL PNEXT ; DB 'Writing..',EOT ; CALL WRITE ;WRITE THE DATA OUT ;We must treat write errors here so that we can handle ignore correctly OR A JR Z,ERT1 ;Skip if no errors ; LD (ERRFLAG),A ;For later abort NXT341: CALL REPORT ; POP HL ;reset update flag ; PUSH HL ; LD (HL),0 ;for abort LD A,0 ;for ignore JR C,NXT349 ;Ignore error JR NZ,WR45 JR NXT349 ;For abort read 'ignore' ; LD A,(ERRFLAG) ; AND 40H ;The write protect status ; JP NZ,EXIT ;Exit if write protected, closing will do no good ; LD HL,0 ; PUSH HL ;Fake a return address to zero after close ; JP CLOSE ;Warn of FATAL error status (update flg set) ; ERT1: LD HL,WRTERRF ;OR THIS WRITE ERROR FLAG IN WITH OR (HL) ;THE REST FROM THIS WRDDB LD (HL),A NXT349: POP HL ;GET CALLERS REG HL BACK LD (HL),A ;Put the BIOS code as update flag ; PUSH AF ; CALL PNEXT ; DB 'Write OK..',EOT ; POP AF OR A RET ; ; RECALL DISK AND TRACK INFO FROM A DDB, PASS IT TO THE BIOS, ; AND SET FLAG SHOWING THAT IS HAS BEEN SENT. ; INTERNALLY REFERENCED ROUTINE. ; RCLDT: LD HL,SENTFLG ;HAS THIS INFO ALREADY BEEN SENT? LD A,(HL) OR A LD A,0 ;*** This used to give an error return RET NZ ;YES - SKIP SENDING AGAIN LD (HL),SENTTOK ;NO - SET FLAG SHOWING IT HAS BEEN SENT LD HL,(TRKPTR) ;GET POINTER TO DRIVE & TRACK INFO ;AND FALL THRU TO SEND IT TO BIOS ; ; SEND DRIVE AND TRACK INFO FROM A DDB TO THE BIOS. ; INTERNALLY REFERENCED ROUTINE. ; SENDDT: PUSH HL ;SAVE TRACK POINTER INC HL ;POINT TO DRIVE LD C,(HL) ;GET DRIVE TO REG C CALL SELECT ;PASS DISK POP HL ;GET TRACK POINTER RET NZ LD C,(HL) ;GET TRACK TO REG C PUSH HL CALL SEEK ;PASS TRACK AND RETURN FROM THERE POP HL RET ; ; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER. ;XFER: ; LD B,SECLEN ;XF1: CALL BANK2 ; LD A,(DE) ; LD C,A ;Save it while ; CALL BANK0 ; LD (HL),C ;Could do an error check here ; INC HL ; INC DE ; DJNZ XF1 ; CALL BANK2 ;So good/bad flag is correct ; RET ; RETURN: LD E,A ;no errors please ; CALL PNEXT ; DB ' Still in BANK2..TOS=',EOT ; EX (SP),HL ; LD C,L ; LD B,H ; EX (SP),HL ; LD HL,0 ; ADD HL,BC ; CALL PUT4HEX ; CALL PNEXT ; DB ' Stack ptr=',EOT ; LD HL,0 ; ADD HL,SP ; CALL PUT4HEX IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD A,E LD SP,(SPSAV3) ; CALL PNEXT ; DB ' Back in BANK0..TOS=',EOT ; EX (SP),HL ; LD C,L ; LD B,H ; EX (SP),HL ; LD HL,0 ; ADD HL,BC ; CALL PUT4HEX ; CALL PNEXT ; DB ' Stack ptr=',EOT ; LD HL,0 ; ADD HL,SP ; CALL PUT4HEX ; ; DI ; HALT ;******************** RET ;BANK2: ; IF NOT NO.CACHE ; IN A,(BITDAT) ; RES 4,A ; OUT (BITDAT),A ; ENDIF ; RET ; ;BANK0: ; IF NOT NO.CACHE ; IN A,(BITDAT) ;Exit, after reselecting main memory ; SET 4,A ; OUT (BITDAT),A ; ENDIF ; RET ; EXIT: ; CALL BANK0 IN A,(BITDAT) SET 4,A OUT (BITDAT),A LD SP,(SPSAV3) JP 0 ; ; \\\\\\\\\\\\\\\\ //////////////// ; >>>>>>>>>>>>>>>> R A M <<<<<<<<<<<<<<<< ; //////////////// A R E A S \\\\\\\\\\\\\\\\ ; ; DDB ADDRESS BUFFERS. EACH BUFFER IS FOUR W-O-R-D-S LONG, ; ONE WORD FOR THE ADDRESS OF THE BUFFER FOR EACH OF FOUR ; POSSIBLE DRIVES. IF THERE IS NO DDB FOR A GIVEN DRIVE, ; THE DDB ADDRESS IS ZERO. ; ENTRY FSTBUF ;to ease debugging make table EXTernal FSTBUF: IF NO.CACHE RDBUF DEFW 0,READ.DDB WRBUF DEFW 0,0 DIRBUF DEFW 0,DIR.DDB ELSE ;For the CACHE buffer we need 1A9DH bytes for each DDB ; Allocate DDBs at 100H,1C00,3700,5200,6D00,8800-A300 leaving 1D00H bytes spare RDBUF DW 100H,1C00H WRBUF DW 3700H,5200H DIRBUF DW 6D00H,8800H ENDIF ;NO.CACHE ; DIRADR DW 0 ;ADDRESS OF CURRENT DIRECTORY DDB RDADR DW 0 ;ADDRESS OF CURRENT READ TRACK DDB WRADR DW 0 ;ADDRESS OF CURRENT WRITE TRACK DDB ; TRKPTR DW 0 ;POINTER TO TRACK & DRIVE INFO IN DDB SENTFLG DB 0 ;ZERO IF TRACK & DRIVE HAVEN'T BEEN SENT WRTERRF DB 0 ;WRITE DDB ERROR FLAG, 0 IF NO ERROR ; REQDSK DB 0FFH REQTRK DB 0FFH REQSEC DB 0FFH REQDMA DW 0FFFFH ; ONLINE DB 0, 0 ;DRIVE ON LINE VECTOR ; ERRFLAG: DB 0 ;Used to indicate write protect error GLOBAL SP3 SP3: SPSAV3: DW 0 ;Old stack pointer addr ; ;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ; Single Density disk driver for InfoSoft on the BigBoard ; (C) Copyright Trevor Marshall, Wait-Aid Ltd, 1981 ;//////////////////////////////////////////////////////// ;////////////////////////////////////////////////////// ; ;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 88H ;READ COMMAND WRTCMD EQU 0A8H ;WRITE COMMAND SKCMD EQU 1CH ;SEEK COMMAND With verify FINCMD EQU 0D0H ;FORCE INTR COMMAND RSTCMD EQU 0CH ;RESTORE COMMAND HLOAD EQU 04H ;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 ; ;B is unit#, CD is trk#, A is surface#, E is sector # MYSET: LD A,D LD (REQTRK),A ;set TRK# ;Trk 0 test will not work here as the READ following will give 10 errors LD A,E ;get sector # LD (REQSEC),A LD C,B ;assume C is zero ( < 255 trks) JP FSETDSK ; ; ;B is unit#, HL is buffer MYSPEC: LD A,C LD C,B CP 3 JP Z,UEJECT ;Let GENDVR do it CP 0 JR Z,DOHOME CP 10 ;Log in disk JP Z,LOGIN XOR A ;Discard all others RET ; DOHOME: CALL SELECT ;select unit RET NZ ;exit on errors JP HOME ;then home drive ; ;HL is DMA ptr, A is command MYIO: LD E,A ;DE not used for variables IN A,(BITDAT) RES 4,A OUT (BITDAT),A LD (SPSAV3),SP LD SP,0BF00H ;in baNk2 LD A,E OR A ;IS A 0? LD (REQDMA),HL ;for FAST buffers JP Z,FREAD JP FWRITE ; S.D.SDT: DB 8 ;Cluster size DW S.D.DUB DW 52 ;Directory start DB 64,2 ;Dir entries,clusters DB 0 ;standard directory ; D.D.SDT: DB 32 ;Cluster size (4K) DW D.D.DUB DW 104 ;Directory start (DOS trk '2') DB 192,2 ;Dir entries,2 x 4K clusters DB 0 ;standard directory ; D.S.SDT: DB 16 ;2k clusters DW D.S.DUB DW 52 ;start of DOS trk 2 DB 128,2 ;2 2k clusters for DIR DB 0 ;standard directory ; D.S.DUB: DW 77*26*2 ;total blocks DB 0 ;3rd size byte DW 77*26*2 ;blocks/surface DB 26 ; " / trk DB 1 ;128s/blk DW -1 ;IBM interleaved DW MYSET,MYIO,MYSPEC ; MYDUB: S.D.DUB: DW 77*26 ;Total Blocks DB 0 ;3rd size byte DW 77*26 ;Blocks/surface DB 26 ; " /trk DB 1 ;128s/blk DW -1 ;IBM interleaving DW MYSET,MYIO,MYSPEC D.D.DUB: DW 77*52*2 ;Total Blocks DB 0 ;3rd size byte DW 77*52*2 ;Blocks/surface DB 52 ; " /trk DB 1 ;128s/blk DW DDINT ;interleaving DW MYSET,MYIO,MYSPEC ;Use a 4 interleave (may be too fast) DDINT: DB 1,5,9,13,17,21,25,29,33,37,41,45,49,2,6,10 DB 14,18,22,26,30,34,38,42,46,50,3,7,11,15,19 DB 23,27,31,35,39,43,47,51,4,8,12,16,20,24,28 DB 32,36,40,44,48,52 ; ;Now log on the disk to see if it is double or single density GLOBAL LOGIN LOGIN: ; ; CALL PNEXT ; DB 'LOGGING,',EOT ; PUSH BC CALL DOCLOSE ; PUSH AF ; CALL PNEXT ; DB 'Returned from CLOSE..',EOT ; POP AF POP BC RET NZ ; CALL SELECT RET NZ ;to error ; CALL TURNON ;After selecting drive BIT 7,A ;TEST DRIVE NOT READY BIT JP NZ,ERRMSG1 ; AND CONTINUE IF ITS READY ; LD HL,SECTOR LD (HL),1 ;select sector #1 ; CALL HOME RET NZ ; LD HL,RECLEN ;Indicate LOGIN to READ SET 7,(HL) ; LD HL,LOGBUF ;Set DMA ; CALL READ RET NZ ; could Continue on here, as a failed sector will default to S/D ;First default the CREATE.DDB routine to S/D LD A,27 ;S/D sectors/trk+1 LD (POKE7+1),A ; LD HL,LOGBUF ;look for 'DS' on the label LD A,'D' CP (HL) JR NZ,SINGLD INC HL LD A,'S' CP (HL) ;if it is then D/S JR NZ,SINGLD ; Also check that it is a D/D disk INC HL LD A,'D' CP (HL) JR NZ,DOUBLES ;Failure here is D/S, S/D INC HL CP (HL) JR NZ,DOUBLES ;It must be double sided double density, now calculate the record size LD A,53 ;Number of sectors/trk+1 LD (POKE7+1),A ;into CREATE.DDB routine ; Load the sector length/2 for read/writes LD A,11B ; D/D & D/S Flags ON JR SD2 ;to store & RET DOUBLES: LD A,10B ; Bit 1 = D/S JR SD2 SINGLD: LD A,00B ; Bits 0, 1 reset SD2: LD (RECLEN),A ;TRACK has already been fixed by HOME ;Now change the SDT and DUB values LD HL,(CSDTE) ; LD A,(RECLEN) BIT 0,A RES 6,(HL) ;default S/D JR Z,NOSET ;if Z it is S/D, otherwise SET 6,(HL) ;set flags bit (update it to D/D) NOSET: LD DE,4 ADD HL,DE ;now pointing at clustersize entry EX DE,HL ;DE is ptr LD B,8 ;SDT entries to update ;Include the Big Directory flag ; LD A,(RECLEN) BIT 1,A ;test D/S flag LD HL,S.D.SDT ;point at SDT JR Z,SETSDT ;set it BIT 0,A ;test D/D flag LD HL,D.S.SDT ;D/S S/D JR Z,SETSDT LD HL,D.D.SDT ;else is D/D SETSDT: LD A,(HL) LD (DE),A INC HL INC DE DJNZ SETSDT SKIPSET: ;Initialize 2 DDBs for Directory and Read, disk A **** Ultimately all DDBs LD HL,ONLINE ;Reset the online tokens LD A,(UNIT) ;GET REQUESTED DRIVE ; LD C,A LD B,0 ADD HL,BC ;Pointing at on line token LD (HL),0 ; offline ; CALL MAKE.DDB JP FSETDSK ; ;A Subroutine used by INIT and SELECT to make all the DDBs MAKE.DDB: LD HL,DIRBUF ;both A & C are unit CALL INDEXA ;get the DIRBUF table entry IF NO.CACHE LD A,L OR H JR Z,VB1 ENDIF LD B,2 ;Directory trk# CALL CREATE.DDB VB1: LD HL,RDBUF CALL INDEXA IF NO.CACHE LD A,L OR H JR Z,VB2 ENDIF LD B,0FFH ;empty track, will not match any requested CALL CREATE.DDB VB2: LD HL,WRBUF CALL INDEXA IF NO.CACHE LD A,L OR H JR Z,VB3 ENDIF LD B,0FFH CALL CREATE.DDB VB3: RET ; ;Enter with unit in C SELECT: ; ; CALL PNEXT ; DB 'Selecting,',EOT ; LD HL,UNIT ;POINT HL TO DRIVE SELECT DATA LD A,(HL) ;LOAD A WITH OLD UNIT# LD (HL),C ; AND STORE NEW UNIT# FROM C ;Check if drive is already logged LD (OLDSCT),A ;save it in RAM SUB C RET Z ;If so, exit ; IN A,(BITDAT) AND 0D8H ;MERGE IN NEW DRIVE UNIT# IN C and side 0 ;Merge side 0 as disk jacket might have wrong index hole punched OR C ; IN PLACE OF THE CURRENT ONE OUT (BITDAT),A ; TO SELECT THE NEW DISK DRIVE CALL TURNON ;After selecting drive AND 81H ;TEST DRIVE NOT READY BIT JR Z,SEL2 ; AND CONTINUE IF ITS READY ; LD B,81H ;Not ready ERRMSG1: LD C,A ; LD A,0FEH ;status RET ; SEL2: LD A,(OLDSCT) ;We will store the old track number and RECLEN for later INC HL ;POINT TO HEAD POSITION TABLE ADD 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,TTLENG ;RECTAB next ADD L LD L,A LD A,(RECLEN) LD (HL),A ;Store it in reclen table ; SEL3: LD HL,RECTAB LD A,L ADD C ;INDEX INTO TABLE TO GET LD L,A ; RECLEN OF NEW DRIVE LD A,(HL) LD (RECLEN),A LD A,-TTLENG ;Now get track data ADD L LD L,A LD A,(HL) ;indexed trk # ; ;Doing LOGINs here fouls up SDT table settings & LABELS NXT111: LD (TRACK),A ;store the current head position NXT112: ;Now delay 33 to 38.5 milliseconds to allow head to settle on new drive LD B,7 ;loop count LP124: IN A,CTC3 ;Get current CTC count value LD C,A ;save it LP123: IN A,CTC3 ;loop until it has changed CP C JR Z,LP123 ;(it may be a 'short' cycle), then DJNZ LP124 ;get a complete count cycle (5.5 msec) XOR A ;No errors RET ; ; ; HOME: ; ; CALL PNEXT ; DB 'Homing,',EOT ; CALL TURNON ;CLEAR DISK CONTROLLER AND 81H ;Mask statuses ; LD B,81H ;error # JR NZ,ERRMSG2 ;on error ; XOR A ;Already 0 LD (TRACK),A ;SET TRACK# IN MEM TO ZERO IN A,(BITDAT) ;Now set up side 0, single D SET 3,A ;S/D RES 5,A ;Side 0 OUT (BITDAT),A RESTOR: LD A,RSTCMD ;LOAD B WITH A RESTORE COMMAND CALL CMDOUT AND 98H ;Mask off TRK 0 status ; LD B,82H ;error # RET Z ERRMSG2: LD C,A ; LD A,0FEH RET ; ; ; SEEK: ; ; CALL PNEXT ; DB 'Seeking,',EOT ; CALL TURNON ;CLEAR DISK CONTROLLER AND A,81H JR NZ,ERRMSG2 ;EXIT IF DRIVE NOT READY no msg LD A,(TRACK) ;Get current track# OUT TRKREG,A ;to the 1771 LD HL,RECLEN ;Dont do next if D/S BIT 1,(HL) ;is it D/S ? 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 ;select 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 ;get new trk # OR A ;On side 0, is the trk # also = 0 ? JR Z,NSKP ;yes, always make trk 0, side 0, S/D ;Here is either side 1 or not trk #0 SK2: BIT 0,(HL) ;(RECLEN) (Z=S/D) ; SET 3,B ;DD* bit is already reset ready for S/D 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, exit 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 ;Now delay 16.5 to 22 milliseconds to allow head to settle PUSH AF ;TANDON TM848 specified at 15 msec settling time LD B,4 ;loop count LP104: IN A,CTC3 ;Get current CTC count value LD C,A ;save it LP103: IN A,CTC3 ;loop until it has changed CP C JR Z,LP103 ;(it may be a 'short' cycle), then DJNZ LP104 ;get a complete count cycle (5.5 msec) POP AF ; LD B,83H ;Error # JR NZ,ERRMSG2 RET ;RETURN FINAL SEEK STATUS IN A ; ;WRTERR: CALL PNEXT ; DB CR,LF,'*** Disk is Write Protected',EOT ; CALL REPORT3 ; JP C,RETURN.OK ;ignore ; JR NZ,WRITE ;retry ; LD A,40H ;The write protect status ; JP RETURN ;otherwise return to WARN and finish close ; ;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 CALL TURNON POP HL ; BIT 6,A ;look only for write protect ; LD B,89H ;Drive is write protected ; JR NZ,WRTERR ;to prompt for fix AND 81H ;Ready? JP NZ,ERRMSG2 ;to DSKERR ; LD B,WRTCMD JR RDWRT READ: ; ; CALL PNEXT ; DB 'Reading,',EOT ; PUSH HL CALL TURNON ;try to omit this BUT reads too slow AND 81H POP HL ; LD B,81H ;error # JP NZ,ERRMSG2 ;EXIT IF DRIVE NOT READY ; LD B,RDCMD RDWRT: LD (IOPTR),HL ;set DMA 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 ;Allow for 128 byte sectors only at present LD B,80H ;128 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 ;See if doing a login (short read) only BIT 7,(HL) LD HL,(IOPTR) ;Set up DMA pointer OUT CMDREG,A ;START the 1771 reading/writing JR NZ,LOGLOOP ;Bit 7 was HI so short read 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 9CH ;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 S/D only LD B,6 ;Bytes to read LPZY3: HALT INI JP NZ,LPZY3 JR RW6 ;For error processing ; WLOOP: HALT OUTI JP NZ,WLOOP CALL IRQ2 AND 0FCH ;MASK OFF AS ABOVE PLUS WRITE FAULT RW3: JR NZ,RW7 RETN ;not conditional RW7: LD C,A ;Save error status in C LD HL,RETRY DEC (HL) ;DECREMENT RE-TRY COUNT AND JR NZ,RW1 ;Execute command again if not=0 ; LD B,86H ;error # read after write LD A,C OR A ;Ensure error flag is set ; LD A,0FEH 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 RESNMI ;Restore NMI IN A,STSREG RETN ; 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 ; RESNMI: LD HL,NMIVEC ;Restore NMI contents LD (HL),D EI RET ; TURNON: LD A,60 ;30 secs LD (MOTOR),A ;RE-LOAD THE MOTOR TURN-OFF TIMER ; Test to see if new drive is ready here IN A,(BITDAT) AND 0FBH ;ELSE RE-ENABLE THE DRIVE SELECTS OUT (BITDAT),A ; AND ACTIVATE THE MOTOR RELAY CALL FORCE BIT 7,A ;Ready? RET Z ; AND EXIT IF STILL TURNED ON 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,20 TURN4: CALL WAIT ;GIVE 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 * ;* * ;**************************************** ; ; ENTRY LOGBUF LOGBUF: DB 0,0,0,0,0,0 DATA ENTRY BGFIFO BGFIFO: BIGFIFO: DS 40H ;----------------------------------------------End of code section ORG 0FF80H ; ENTRY VECTAB ; MEM: EQU $ ;Start of RAM area ; VECTAB: EQU $ ;INTERRUPT VECTOR TABLE STARTS HERE SIOVEC: EQU $ ;SPACE FOR 8 VECTORS FOR SIO ;Read these DS equivalents for LINKA as.... ; var DS | CTCVEC: EQU SIOVEC+16 ;SPACE FOR 4 VECTORS FOR CTC SYSVEC: EQU CTCVEC+8 ;SPACE FOR 2 VECTORS FOR SYSTEM PIO GENVEC: EQU SYSVEC+4 ;SPACE FOR 2 VECTORS FOR GENERAL PIO ; ; ; DISK I/O DRIVER VARIABLES ; Be careful not to straddle a Page Boundary with these UNIT: EQU GENVEC+4 ;CURRENTLY SELECTED DISK# TRKTAB: EQU UNIT+1 ;2 DRIVE HEAD POSITION TABLE TTLENG: EQU 2 RECTAB: EQU TRKTAB+2 ;2 Drive record length table MOTOR: EQU RECTAB+2 ;DRIVE MOTOR TURN-OFF TIMER RECLEN: EQU MOTOR+1 ;Current disk 'Record Length' and density flag TRACK: EQU RECLEN+1 SECTOR: EQU TRACK+1 CMDTYP: EQU SECTOR+1 ;COMMAND BYTE FOR READS/WRITES RETRY: EQU CMDTYP+1 ;DISK OPERATION RE-TRY COUNT IOPTR: EQU RETRY+1 ;DISK I/O BUFFER POINTER OLDPTR: EQU IOPTR+2 ;A scratch location used by RESTORE RWFLAG: EQU OLDPTR+2 ;Discriminates reads/writes OLDSCT: EQU RWFLAG+1 ;For saving the old sector # in LOGIN ; ; ; ; KEYBOARD DATA INPUT FIFO VARIABLES ;FIFO: EQU OLDSCT+1 ;CONSOLE INPUT FIFO ;FIFCNT: EQU FIFO+16 ;FIFO DATA COUNTER ;FIFO is now in RAM for type ahead buffer allocation FIFCNT: EQU OLDSCT+1 FIFIN: EQU FIFCNT+1 ;FIFI INPUT POINTER FIFOUT: EQU FIFIN+1 ;FIFO OUTPUT POINTER ; CLOSE.TICKS: EQU FIFOUT+1 ;2 locs for CLOSE elapsed timer ; ; STACK POINTER SAVE AND LOCAL STACK FOR INTERRUPT ROUTINES ;TMPSTK: EQU FIFOUT+1+16 ;Internal stack for interrupts TMPSTK: EQU CLOSE.TICKS+2+16 STACK EQU TMPSTK+16 ;Stack for INIT SPSAVE: EQU STACK+2 ;for saving SP SPSAV2: EQU SPSAVE+2 ;For saving IOS SP ; ; ; 'SOFTWARE' VECTORS FOR INTERRUPT SERVICE ROUTINES TIKVEC: EQU SPSAV2+2 ;ONE SECOND INTERRUPT ROUTINE VECTOR PINVEC: EQU TIKVEC+2 ;PARALLEL CONSOLE INPUT VECTOR SINVEC: EQU PINVEC+2 ;SERIAL CONSOLE INPUT VECTOR ; ; ; CLOCK-TIMER INTERRUPT VARIABLES TIKCNT: EQU SINVEC+2 ;BINARY CLOCK TICK COUNTER DAY: EQU TIKCNT+2 ;CALENDAR DAY MONTH: EQU DAY+1 ; MONTH YEAR: EQU MONTH+1 ; YEAR HRS: EQU YEAR+1 ;CLOCK HOURS REGISTER MINS: EQU HRS+1 ; MINUTES RETISTER SECS: EQU MINS+1 ; SECONDS REGISTER ; ; CRT OUTPUT DRIVER VARIABLES COFLAG: EQU SECS+1 ;used by switchstash CURSOR: EQU COFLAG+1 ;CURSOR POINTER CHRSAV: EQU CURSOR+2 ;CHARACTER OVERLAYED BY CURSOR CSRCHR: EQU CHRSAV+1 ;CHARACTER USED FOR A CURSOR BASE: EQU CSRCHR+1 ;CURRENT CONTENTS OF SCROLL REGISTER LEADIN: EQU BASE+1 ;STATE OF LEAD-IN SEQUENCE HANDLER DELAY.FLG: EQU LEADIN+1 ;1 if 100Usec delay is required before display ; ; MLNGTH EQU LEADIN+2-MEM ;spare loc for err in zeroing RAM ; ;----------------------------------------------------------------- IF NO.CACHE DATA ENTRY DDB ;debugging DDB: READ.DDB: DS 1 ;TRACK# DS 1 ;DRIVE# ;Now define storage for 52 of the folowing formats ; [DB SECTOR ; DB 0 ;Update flag ; DS 80H ;Data ] DS 52*[128+3] + 1 ;include DB 0 ;End marker (sector # of 0) ; DIR.DDB: DS 1 ;Track# DS 1 ;Drive# DS 52*[128+3] + 1 ;NOTE: we don't need this much ******** ;include DB 0 ;End marker ENDIF ;NO.CACHE ; END