; CP/M BASIC INPUT/OUTPUT OPERATING SYSTEM (BIOS) ; TARBELL ELECTRONICS ; 2.2 VERSION OF 2-19-80 ; CHANGED SIGN-ON FOR CPM 2.2 2-19-80 ; ; THIS MODULE CONTAINS ALL THE INPUT/OUTPUT ; ROUTINES FOR THE CP/M SYSTEM, INCLUDING ; THE DISK ROUTINES. ; ; THIS SECTION DEFINES THE I/O PORTS AND ; STATUS BITS. BY SETTING THE PROPER VALUES ; FOR THE EQU STATEMENTS, THE I/O MAY BE ; AUTOMATICALLY RECONFIGURED TO FIT MOST ; SITUATIONS. THE TRUE AND FALSE ONES ; CONTROL CONDITIONAL ASSEMBLIES OF DIFFERENT ; SECTIONS OF I/O ROUTINES TO FIT DIFFERENT ; INTERFACE REQUIREMENTS. TRUE EQU 0FFFFH ;DEFINE VALUE OF TRUE. FALSE EQU NOT TRUE ;DEFINE VALUE OF FALSE. ;*************************************************** ;*** THIS BEGINS THE AREA WHICH REQUIRES CHANGES *** ;*** FOR DIFFERENT CONSOLE I/O SYSTEMS *** ;*************************************************** MSIZE EQU 24 ;MEMORY SIZE IN KBYTES. INTRP EQU FALSE ;TRUE IF INTERRUPTS ALLOWED. STD EQU TRUE ;TRUE IF STANDARD I/O. MSIO2 EQU FALSE ;TRUE IF MITS 2SIO. DELTA EQU FALSE ;TRUE IF USING DELTA PRODUCTS CPU. ISIO2 EQU FALSE ;TRUE IF IMSAI SIO-2. TUART EQU FALSE ;TRUE IF CROMEMCO TUART. VIDEO EQU FALSE ;TRUE IF USING A MEMORY MAPPED VIDEO. OTHER EQU FALSE ;TRUE IF SOMETHING ELSE. SOLOS EQU FALSE ;TRUE IF PROC TECH SOLOS. DUBSID EQU FALSE ;TRUE FOR DOUBLE SIDED DRIVES. SPOOL EQU FALSE ;TRUE IF USING KLH SPOOLER. NDISK EQU 4 ;DEFINES THE NUMBER DRIVES IN SYSTEM. IF VIDEO ;IF USING A VIDEO BOARD OUTADDR EQU 0 ;PUT OUTPUT ADDRESS HERE ENDIF IF NOT SOLOS ;IF NOT PROC TECH SOLOS, CSTAT EQU 0 ;CONSOLE STATUS PORT. ENDIF CCOM EQU 0 ;CONSOLE COMMAND PORT. CDATA EQU 1 ;CONSOLE DATA PORT. CONUL EQU FALSE ;CONSOLE NULLS? CNULL EQU 0 ;CONSOLE NULL COUNT. LSTAT EQU 2 ;LIST STATUS PORT. LCOM EQU 2 ;LIST COMMAND PORT. LDATA EQU 3 ;LIST DATA PORT. LSTNUL EQU FALSE ;LIST DEVICE NULLS? LNULL EQU 0 ;LIST NULL COUNT. LSTPAG EQU FALSE ;LIST DEVICE PAGING? LINCNT EQU 66 ;LINES PER PAGE. HLAB EQU 0 ;8 FOR HD LD AT BEG OF SEEK. STPRAT EQU 1 ;RATE 1=6MS, 2=10MS, 3=20MS. DUAL EQU FALSE ;TRUE IF DUAL DRIVE. PERSCI EQU FALSE ;TRUE IF FAST SEEK (PERSCI). ;******************************************************* ;*** THIS IS THE END OF THE AREA WHICH NORMALLY NEED *** ;*** BE CHANGED FOR MOST CONSOLE I/O SYSTEMS *** ;******************************************************* RDYLO EQU STD OR SOLOS OR OTHER ;STATUS READY WHEN LOW. RDYHI EQU NOT RDYLO IF SOLOS ;IF PROC TECH SOLOS, CSTAT EQU 0FAH ;CONSOLE STATUS PORT. KBD EQU 0C02EH ;SOLOS KEYBOARD. CLRSCR EQU 0C0D5H ;CLEAR SCREEN. SCRN EQU 0C054H ;SOLOS OUTPUT. ENDIF IF NOT SOLOS ;IF NOT PROC TECH SOLOS, DISK EQU 0F8H ;DISK BASE ADDRESS. ENDIF IF SOLOS ;IF PROC TECH SOLOS, DISK EQU 0E8H ;DIFFERENT DISK PORTS. ENDIF DCOM EQU DISK ;DISK COMMAND PORT. DSTAT EQU DISK ;DISK STATUS PORT. TRACK EQU DISK+1 ;DISK TRACK PORT. SECTP EQU DISK+2 ;DISK SECTOR PORT. DDATA EQU DISK+3 ;DISK DATA PORT. WAIT EQU DISK+4 ;DISK WAIT PORT. DCONT EQU DISK+4 ;DISK CONTROL PORT. RTCNT EQU 10 ;RETRY COUNT. IF STD ;IF STANDARD I/O, CKBR EQU 00000001B ;KEYBOARD READY BIT. CPTR EQU 10000000B ;CONS OUTPUT RDY BIT. ENDIF IF MSIO2 ;IF MITS 2SIO, CKBR EQU 00000001B ;KEYBOARD READY BIT. CPTR EQU 00000010B ;PRINT READY BIT. ENDIF IF ISIO2 OR DELTA CKBR EQU 00000010B ;KEYBOARD READY BIT. CPTR EQU 00000001B ;PRINT READY BIT. ENDIF IF TUART ;IF CROMEMCO TUART, CKBR EQU 01000000B ;KEYBOARD READY BIT. CPTR EQU 10000000B ;PRINT READY BIT. ENDIF IF SOLOS ;IF PROC TECH SOLOS, CKBR EQU 00000001B ;KEYBOARD READY BIT. CPTR EQU 10000000B ;DUMMY EQU. ENDIF IF OTHER ;IF SOMETHING ELSE, CKBR EQU 00000010B ;KEYBOARD READY BIT. CPTR EQU 10000000B ;PRINTER READY BIT. ENDIF LRBIT EQU CPTR ;LISTER READY BIT. IOBYTE EQU 3 ;ADDRESS OF I/O BYTE. CBASE EQU (MSIZE-20)*1024 ;BIAS FOR LARGER THAN 20K. CPMB EQU CBASE+3400H ;START OF CPM 2.0 BDOS EQU CPMB+806H ;START OF BDOS 2.0. BIOS EQU CPMB+1600H ;START OF CBIOS IO. CDISK EQU 4 ;LOCATION 4 IS CURRENT DISK. NSECTS EQU 44 ;NUMBER OF SECTORS IN IT. ORG BIOS ;START OF CBIOS STRUCTURE. ; ; I/O JUMP VECTOR ; THIS IS WHERE CPM CALLS WHENEVER IT NEEDS ; TO DO ANY INPUT/OUTPUT OPERATION. ; USER PROGRAMS MAY USE THESE ENTRY POINTS ; ALSO, BUT NOTE THAT THE LOCATION OF THIS ; VECTOR CHANGES WITH THE MEMORY SIZE. ; JMP BOOT ;FROM COLD START LOADER. WBOOTE: JMP WBOOT ;FROM WARM BOOT. JMP CONST ;CHECK CONSOLE KB STATUS. JMP CONIN ;READ CONSOLE CHARACTER. JMP CONOT ;WRITE CONSOLE CHARACTER. JMP LIST ;WRITE LISTING CHAR. JMP PUNCH ;WRITE PUNCH CHAR. JMP READER ;READ READER CHAR. JMP HOME ;MOVE DISK TO TRACK ZERO. JMP SELDSK ;SELECT DISK DRIVE. JMP SETTRK ;SEEK TO TRACK IN REG A. JMP SETSEC ;SET SECTOR NUMBER. JMP SETDMA ;SET DISK STARTING ADR. JMP READ ;READ SELECTED SECTOR. JMP WRITE ;WRITE SELECTED SECTOR. JMP PRSTAT ;LIST STATUS CHECK. JMP SECTRAN ;SECTOR TRANSLATE ROUTINE. ; THESE ENTRY POINTS ADDED BY TARBELL ELECTRONICS. IF SPOOL ;IF USING KLH SPOOLER. DB 0FFH ;FLAG FOR SPOOLER. DW LTBSY ;LISTER STATUS LOCATION DW LTBSY ;FOR SPOOLER - - DW LTBSY ;I DON'T KNOW WHY IT'S DW LTBSY ;HERE 4 TIMES EITHER. ENDIF ; ; THIS SECTION DEFINES THE THE DISK PARAMETERS ; NOTE: ; IF YOU HAVE THE MACRO ASSEMBLER (MAC) FROM ; DIGITAL RESEARCH, YOU MAY ELIMINATE THIS SECTION ; STARTING AT **AA** TO **BB** AND USE THE MACRO ; FILE "DISKDEF" TO CUSTOM TAILOR YOUR SYSTEM TO ; ALLOW DIFFERENT TYPES OF DRIVES OR MORE THAN 4 ; DRIVES. ; ;**AA** ; DPBASE EQU $ ;BASE OF DISK PARAMETER BLOCK DPE0: DW XLT0,0000H ;TRANSLATE TABLE DW 0000H,0000H ;SCRATCH AREA DW DIRBUF,DPB0 ;DIR BUFF, PARM BLOCK DW CSV0,ALV0 ;CHECK, ALLOC VECTORS ; DPE1: DW XLT1,0000H DW 0000H,0000H DW DIRBUF,DPB1 DW CSV1,ALV1 ; DPE2: DW XLT2,0000H DW 0000H,0000H DW DIRBUF,DPB2 DW CSV2,ALV2 ; DPE3: DW XLT3,0000H DW 0000H,0000H DW DIRBUF,DPB3 DW CSV3,ALV3 ; ;THE FOLLOWING DESCRIBES THE DISK PHYSICAL ;NATURE, SUCH AS SECTORS/TRACK,DIRECTORY SIZE. ; DPB0 EQU $ ;ONE OF 4 DISK PARM. BLOCKS DW 26 ;SECTORS/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 ;NUMBER OF SYSTEM TRACKS ; ;SECTOR TRANSLATION TABLE ; XLT0 EQU $ ;START OF TRANS. TABLE DB 1,7,13,19,25 DB 5,11,17,23,3 DB 9,15,21,2,8 DB 14,20,26,6,12 DB 18,24,4,10,16,22 ; DPB1 EQU DPB0 ;EQUIVALENT PARAMETERS XLT1 EQU XLT0 ;SAME TRANSLATE TABLE ; DPB2 EQU DPB0 XLT2 EQU XLT0 ; DPB3 EQU DPB0 XLT3 EQU XLT0 ; ;**BB** ; ; ; BOOT ; THIS SECTION IS EXECUTED WHENEVER RESET AND RUN ; IS PUSHED, AFTER THE COLDSTART LOADER READS IN ; THE CPM SYSTEM. ; BOOT: LXI SP,80H ;SET STACK POINTER. IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ENABLE THEM HERE. ENDIF IF STD ;IF STANDARD I/O, DW 0,0 ;LEAVE SPACE FOR INIT. DW 0,0 DW 0,0 DW 0,0 ENDIF IF MSIO2 ;IF MITS 2SIO, MVI A,3 ;INITIALIZE 2SIO. OUT CCOM OUT LCOM MVI A,11H OUT CCOM OUT LCOM ENDIF IF ISIO2 OR DELTA MVI A,0AAH ;INITIALIZE SIO 2-2. OUT CCOM OUT LCOM MVI A,40H OUT CCOM OUT LCOM MVI A,0CEH OUT CCOM OUT LCOM MVI A,37H OUT CCOM OUT LCOM ENDIF IF TUART ;IF CROMEMCO TUART, MVI A,1 ;SET A = 1. OUT 54H ;SELECT DEVICE A. OUT 52H ;RESET DEVICE B. LXI H,BAUDRS ;GET ADR OF BAUD RATE TABLE. MVI A,11H ;OCTUPLE THE CLOCK. IT1: OUT 02H ;& RESET CURRENT DEV. MOV A,M ;GET BAUD RATE FROM TABLE. INX H ;INCREMENT POINTER. OUT 0 ;SET BAUD RATE. CALL CONIN ;READ KEYBOARD. CALL CONIN ;READ KEYBOARD AGAIN. CPI 0DH ;IF NOT CARRIAGE-RETURN, MVI A,1 ;SLOW THE CLOCK. JNZ IT1 ;UNTIL A CARRIAGE-RETURN. ENDIF IF SOLOS ;IF PROC TECH SOLOS, CALL CLRSCR ;CLEAR SCREEN. ENDIF XRA A ;CLEAR SCRATCH AREA. STA IOBYTE ;CLEAR I/O BYTE. STA CDISK ;SELECT DRIVE ZERO MVI C,ENDZ-STARTZ ;GET LENGTH OF ZERO AREA. LXI H,STARTZ ;GET SCRATCH ADDRESS. BOOTL: MOV M,A ;PUT ZERO IN MEMORY. INX H ;INCREMENT POINTER. DCR C ;DECREMENT COUNTER. JNZ BOOTL ;LOOP TILL DONE. MVI A,0F2H ;SET LATCH CODE = F2. STA LATCH CALL SETUP ;SET UP JUMPS. IN CDATA ;CLEAR CONSOLE STATUS. LXI H,SMSG ;PRINT OPENING MESSAGE. CALL PMSG GOCPM: LDA CDISK ;GET DISK NUMBER TO MOV C,A ;PASS TO CCP IN C. JMP CPMB ;JUMP TO CCP. ; ; SET UP JUMPS INTO CP/M IN LOWER MEMORY. ; SETUP: MVI A,0C3H ;PUT JMP TO WBOOT STA 0 ;ADR AT ZERO. LXI H,WBOOTE SHLD 1 STA 5 LXI H,BDOS ;PUT JUMP TO BDOS SHLD 6 ;AT ADR 5,6,7. LXI H,80H ;SET DEFAULT DMA ADR. SHLD DMAADD RET ;RETURN FROM SETUP. IF TUART ;IF CROMEMCO TUART, BAUDRS: DB 94H,0CEH,0A2H,92H,88H,84H,82H,1 ENDIF ; ; WARM-BOOT: READ ALL OF CPM BACK IN ; EXCEPT BIOS, THEN JUMP TO CCP. ; WBOOT: LXI SP,80H ;SET STACK POINTER. IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ALLOW THEM HERE. ENDIF IF LSTPAG ;IF LIST DEVICE PAGING, XRA A ;RESET LINE-FEED COUNT. STA LFCNT ENDIF LDA CDISK ;SAVE DISK NUMBER. STA TEMP MVI C,0 ;SELECT DISK ZERO. CALL SELDSK CALL HOME ;MOVE TO TRACK ZERO. JNZ RDERR ;IF ERROR, PRINT MESSAGE. MVI D,NSECTS ;GET # SECTORS FOR CPM READ. LXI B,2 ;TRACK (B)=0, SECTOR (C)=2. LXI H,CPMB ;GET STARTING ADDRESS. IF INTRP ;IF INTERRUPTS ALLOWED, DI ;DISABLE THEM HERE. ENDIF RDBLK: PUSH B ;SAVE B&C. MOV C,B ;GO TO TRACK IN B. CALL SETTRK POP B ;RESTORE B&C. JNZ RDERR ;IF ERROR, PRINT MESSAGE. RBLK1: SHLD DMAADD ;SET STARTING ADDRESS. CALL SETSEC ;READ STARTING AT SECTOR IN C. CALL READ JNZ RDERR ;IF ERROR, PRINT MESSAGE. DCR D ;DECREMENT SECTOR COUNT. JZ ALDON ;ALL DONE WHEN D=0. INR C ;INCREMENT SECTOR NUMBER. MOV A,C ;IF SECTOR NUMBER CPI 27 ;IS NOT 27, JC RBLK1 ;KEEP READING ON THIS TRACK. MVI C,1 ;OTHERWISE, RESET SECTOR=1, INR B ;INCREMENT TRACK NUMBER, JMP RDBLK ;AND READ NEXT TRACK. ALDON: LDA TEMP ;RESTORE DISK NUMBER. IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ALLOW THEM AGAIN HERE. ENDIF STA CDISK CALL SETUP ;SET UP JUMPS. JMP GOCPM ;GO BACK TO CPM. ; RDERR: MVI C,'B' ;GET BOOT ERROR CODE. CALL CONOT ;PRINT IT. CALL CONIN ;READ A CHAR FROM CONSOLE. JMP WBOOT ;DO A WARM BOOT. ; ; CHECK CONSOLE INPUT STATUS. ; CONST: IN CSTAT ;READ CONSOLE STATUS. ANI CKBR ;LOOK AT KB READY BIT. CONST1: MVI A,0 ;SET A=0 FOR RETURN. IF RDYLO ;IF STATUS READY LOW, RNZ ;NOT READY WHEN NOT 0. ENDIF IF RDYHI ;IF STATUS READY HIGH, RZ ;NOT READY WHEN ZERO. ENDIF CMA ;IF READY A=FF. RET ;RETURN FROM CONST. ; ; READ A CHARACTER FROM CONSOLE. ; CONIN: IF NOT SOLOS ;IF NOT PROC TECH SOLOS, IN CSTAT ;READ CONSOLE STATUS. ANI CKBR ;IF NOT READY, ENDIF IF SOLOS ;IF PROC TECH SOLOS, CALL KBD ;READ SOL KEYBOARD. JZ CONIN ;READY WHEN NOT ZERO. ENDIF IF RDYLO AND NOT SOLOS JNZ CONIN ;LOOP UNTIL LOW. ENDIF IF RDYHI ;IF READY WHEN HIGH, JZ CONIN ;LOOP UNTIL HIGH. ENDIF IF NOT SOLOS ;IF NOT PROC TECH SOLOS, IN CDATA ;READ A CHARACTER. ENDIF ANI 7FH ;MAKE MOST SIG. BIT = 0. RET ;RETURN FROM CONIN. ; ; WRITE A CHARACTER TO THE CONSOLE DEVICE. ; CONOT: IF CONUL ;IF NULLS REQUIRED, MVI A,0DH ;IF IT'S A CR, CMP C ;THEN HOP OUT JZ CONULL ;TO NULL ROUTINE. ENDIF CONOT1: IF NOT SOLOS AND NOT VIDEO IN CSTAT ;READ CONSOLE STATUS. ANI CPTR ;IF NOT READY, ENDIF IF RDYLO AND NOT SOLOS AND NOT VIDEO JNZ CONOT1 ;LOOP UNTIL LOW. ENDIF IF RDYHI ;IF READY WHEN HIGH, JZ CONOT1 ;LOOP UNTIL HIGH. ENDIF IF NOT SOLOS AND NOT VIDEO MOV A,C ;GET CHARACTER. OUT CDATA ;PRINT IT. RET ;RETURN. ENDIF ; ;THIS ROUTINE CALLES YOUR VIDEO DRIVER ;ROUTINE WHICH MUST BE IN ROM. ;ALL REGISTERS MUST BE SAVED AND RESTORED ;BY YOUR VIDEO DRIVER IN ORDER TO BE ;COMPATIABLE WITH CPM. ;CPM PASSES THE CHAR. TO BE OUTPUT IN THE ; C REGISTER. MAKE ANY CHANGES IN THIS ;ROUTINE TO PASS THE CHAR FROM REG C TO ;THE REGISTER YOUR VIDEO DRIVER EXPECTS ;IT TO BE IN. ; IF VIDEO ;IF USING A VIDEO DRIVER IN ROM. MOV A,C ;GET THE CPM CHAR INTO REG A CALL OUTADDR ;CALL YOUR VIDEO DRIVER. RET ;RETURN TO CPM. ENDIF IF CONUL CONULL: PUSH B ;SAVE B&C. MVI B,CNULL+1 ;GET NULL COUNT. CONUL1: CALL CONOT1 ;PRINT CR. MVI C,0 ;GET NULL CHAR. DCR B ;DECREMENT COUNTER. JNZ CONUL1 ;DO NEXT NULL. POP B ;RESTORE B&C. MOV A,C ;RESTORE A. RET ;RETURN. ENDIF IF SOLOS ;IF PROC TECH SOLOS, PUSH B ;SAVE B&C. MOV B,C ;PUT CHAR IN B REG. CALL SCRN ;OUTPUT CHAR TO SOLOS. POP B ;RESTORE B&C. MOV A,C ;PUT CHAR IN A. RET ;RETURN FROM CONOT. ENDIF ; ; SELECT DISK NUMBER ACCORDING TO REGISTER C. ; SELDSK: LXI H,0 ;SET UP FOR ERROR CODE MOV A,C ;GET NEW DRIVE. CPI NDISK ;CALLING UNDEFINED DRIVE ? RNC ;IF NO CY, H,L TELLS CPM YES. STA PDISK ;WE ARE OK, SAVE NEW DISK NUMB. LXI H,DISKNO ;GET OLD DRIVE NUMBER. SELMOR: MOV A,M ;GET OLD DISK NUMBER. IF DUAL ;IF DUAL DRIVE, ANI 0FEH ;CLEAR OUT BIT 0. ENDIF MOV E,A ;PUT OLD DISK NO. IN D&E. MVI D,0 LXI H,TRTAB ;GET ADDRESS OF TRACK TABLE. PUSH H ;SAVE ADDRESS OF TRTAB. DAD D ;ADD DISK NO. TO ADDRESS. IN TRACK ;READ 1771 TRACK REGISTER. MOV M,A ;PUT INTO TABLE. MOV A,C ;GET NEW DISK NUMBER. IF DUAL ;IF A DUAL DRIVE, ANI 0FEH ;CLEAR BIT 0. ENDIF MOV E,A ;PUT NEW DISK NO. IN D&E. POP H ;RESTORE ADDRESS OF TRTAB. DAD D ;ADD DISK NO. TO ADDRESS. MOV A,M ;GET NEW TRACK NUMBER. OUT TRACK ;PUT INTO 1771 TRACK REG. MOV A,C ;UPDATE OLD DISK NUMBER. STA DISKNO CMA ;BITS INVERTED INTO LATCH. ADD A ;PUT BITS 1&2 AT 4&5. ADD A ADD A ADD A ORI 2 ;MAKE LATCH COMMAND. STA LATCH ;SAVE NEW LATCH CODE. ; ;SELECT DRIVE AS A FUNCTION OF H,L ; LHLD PDISK ;LOAD DISK NUMBER AND ZERO BYTE LXI D,DPBASE ;POINT TO DISK PARM START. DAD H ;*2 DAD H ; *4 DAD H ; *8 DAD H ; *16 DAD D ;COMPUTE INDEX FOR THE DRIVE XRA A ;SET A = 0. RET ;RETURN FROM SELDSK. ; ; MOVE DISK TO TRACK ZERO. ; HOME: MVI C,0 ;SEEK TO TRACK ZERO. ; ; SET TRACK NUMBER TO WHATEVER IS IN REGISTER C. ; ALSO PERFORM MOVE TO THE CORRECT TRACK (SEEK). ; SETTRK: IF NOT DUBSID ;IF NOT DOUBLE-SIDED, PUSH H ;SAVE H&L. LHLD LATCH ;GET NEW & OLD LATCH. MOV A,L ;GET NEW LATCH. OUT DCONT ;SELECT DRIVE NOW. STA CLATCH ;REMEMBER CURRENT LATCH. CMP H ;IS NEW SAME AS OLD? MVI A,0FFH ;IF NOT, SET FLAG = FF. JNZ SFLAG CMA ;IF NEW = OLD, FLAG = 0. SFLAG: STA HLSF ;SET HEAD-LOAD/SELECT FLAG. POP H ;RESTORE H&L. ENDIF MOV A,C ;GET NEW TRACK NUMBER. IF DUBSID ;IF DOUBLE-SIDED DISK, RRC ;SHIFT RIGHT ONCE. PUSH PSW ;SAVE REVISED TRACK NUMBER. PUSH H ;SAVE H&L. LHLD LATCH ;GET NEW & OLD LATCH. MOV A,L ;PUT NEW LATCH IN A. JC SIDE2 ;IF TRACK # ODD, SIDE #2. ORI 40H ;CLEAR LATCH BIT FOR SIDE 1. JMP SETLAT ;GO AHEAD AND SET LATCH. SIDE2: ANI 0B2H ;SET LATCH BIT FOR SIDE 2. SETLAT: OUT DCONT ;OUTPUT TO THE LATCH. STA CLATCH ;SET CURRENT LATCH CODE. XRA H ;COMPARE OLD WITH NEW, ANI 0BFH ;IGNORE SIDE BIT. MVI A,0FFH ;IF OLD NOT = NEW, JNZ SETFL ; SET FLAG = FF. CMA A ;IF OLD = NEW, FLAG = 0. SETFL: STA HLSF ;SET FLAG ZERO IF SAME. POP H ;RESTORE H&L. POP PSW ;RESTORE THE TRACK NUMBER. ANI 7FH ;CLEAR MSB. MOV C,A ;SET C=TRACK NUMBER. ENDIF STA TRK ;UPDATE OLD WITH NEW. ; ; MOVE THE HEAD TO THE TRACK IN REGISTER A. ; SEEK: PUSH B ;SAVE B&C. MOV B,A ;SAVE DESTINATION TRACK. MVI A,RTCNT ;GET RETRY COUNT. SRETRY: STA SERCNT ;STORE IN ERROR COUNTER. IN TRACK ;READ PRESENT TRACK NO. MOV C,A ;SOME MOV A,C ; DELAY CMP B ;SAME AS NEW TRACK NO.? JNZ NOTHR ;JUMP IF NOT THERE. THERE: POP B ;RESTORE B&C. RET ;RETURN FROM SEEK. NOTHR: ; ;THIS ROUTINE IS TO ALLOW TIME FOR THE DRIVE ;TUNNEL ERASE TO TERMINATE BEFORE MOVING THE ;HEAD. THE DELAY IS APPROX. 700 MICRO-SEC. @ ;4 MHZ CPU TIME, AND DOUBLE THIS FOR 2 MHZ CPU'S. ; PUSH PSW ;SAVE ACCUM MVI A,0D0H ;DELAY COUNT = 208 BUSY1: DCR A ;DECREASE COUNT JNZ BUSY1 ;LOOP TILL DONE POP PSW ;RESTORE ACCUM IF NOT PERSCI ;IF NOT PERSCI DRIVE, MOV A,B ;RESTORE A FROM B. OUT DDATA ;TRACK TO DATA REGISTER. BUSY: IN DSTAT ;READ DISK STATUS. RRC ;LOOK AT BIT 0. JC BUSY ;WAIT TILL NOT BUSY. MVI A,14H+STPRAT+HLAB ;GET STEP RATE, DO OUT DCOM ;SEEK WITH VERIFY. IN WAIT ;WAIT FOR INTRQ. IN DSTAT ;READ STATUS. ANI 91H ;LOOK AT BITS. JZ THERE ;OK IF ZERO. ENDIF IF PERSCI ;IF PERSCI DRIVE, MVI A,40H+HLAB ;IF CARRY = 1, JC SDIR ;STEP IN. MVI A,60H+HLAB ;OTHERWISE, OUT. SDIR: OUT DCOM ;ISSUE STEP DIRECTION. MVI A,20 ;DELAY LOOP COUNT. DLOOP: DCR A ;DECREMENT COUNTER. JNZ DLOOP MOV A,C ;GET PRESENT TRACK. SUB B ;FIGURE TRACKS TO STEP. JP STEP ;IF NEGATIVE, CMA ;FIGURE THE INR A ;TWO'S COMPLEMENT. STEP: MOV C,A ;GET DIFFERENCE. MVI A,1 ;PERSCI STEP COMMAND. STEP1: OUT DCONT ;STEP PERSCI (E-14). DCR C ;COUNT THE STEP. JNZ STEP1 ;STEP UNTIL C = 0. IN WAIT ;CLEAR 1771. IN DSTAT MOV A,B ;GET DEST. TRACK. OUT TRACK ;UPDATE TRACK REG. LDA CLATCH ;GET LATCH CODE. ANI 72H ;MAKE COMMAND TO OUT DCONT ;SWITCH WAIT FOR IN WAIT ;SEEK COMPLETE. LDA CLATCH ;RESTORE LATCH TO OUT DCONT ;OLD CODE. XRA A ;MAKE GOOD RETURN. POP B ;RESTORE B&C. RET ENDIF IF NOT PERSCI ;IF NOT PERSCI DRIVE, LDA SERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ SRETRY ;RETRY SEEK. POP B ;RESTORE B&C. CALL RECOV ;IF SEEK RETRY = 10 CHECK JMP SEEK ; FOR CNTL-C FOR ABORT. ENDIF ; ; SET DISK SECTOR NUMBER. ; SETSEC: MOV A,C ;GET SECTOR NUMBER. STA SECT ;PUT AT SECT # ADDRESS. RET ;RETURN FROM SETSEC. ; ;TRANSLATE THE SECTOR GIVEN B,C ;USING THE TRANSLATE TABLE ;GIVEN BY D,E ; SECTRAN: XCHG ;H,L = TRANS DAD B ;H,L = TRANS (SECTOR) MOV L,M ;L = TRANS (SECTOR) MVI H,0 ;CLEAR REG H RET ;H,L = TRANSLATED SECTOR ; ; SET DISK DMA ADDRESS. ; SETDMA: MOV H,B ;MOVE B&C TO H&L. MOV L,C SHLD DMAADD ;PUT AT DMA ADR ADDRESS. RET ;RETURN FROM SETDMA. ; ; HDLD - GET HEAD-LOAD BIT IF REQUIRED. ; HDLD: LDA HLSF ;GET HEAD-LOAD FLAG. ORA A ;IS A = ZERO? JZ HDLD1 ;HOP IF SO. CMA ;SET A = 0. STA HLSF ;SET FLAG = 0 IF NOT. ; ;IF CHANGING TO A NEW DRIVE, PERFORM A SEEK TO ;THE SAME TRACK TO ALLOW THE HEAD TO UNLOAD. ; IN TRACK ;GET PRESENT TRACK OUT DDATA ;AND TELL 1771 ABOUT IT. MVI A,14H+STPRAT+HLAB ;GET THE STEP RATE. OUT DCOM ;SEND IT TO FLOPPY CONTROLLER. IN WAIT ;WAIT FOR INTRQ. HDLDY: MVI A,4 ;SET BIT TO LOAD HEAD. RET ;RETURN FROM HDLD. HDLD1: IN DSTAT ;READ 1771 STATUS. ANI 20H ;LOOK AT HL BIT. JZ HDLDY ;LOAD IF NOT LOADED. XRA A ;OTHERWISE, A=0. RET ;RETURN FROM HDLD. ; ; READ THE SECTOR AT SECT, FROM THE PRESENT TRACK. ; USE STARTING ADDRESS AT DMAADD. ; READ: MVI A,RTCNT ;GET RETRY COUNT. RRETRY: CALL DSKSET ;SET UP DISK. ADI 88H ;ADD CODE FOR READ SECT. READE: OUT DCOM ;SEND COMMAND TO 1771. RLOOP: IN WAIT ;WAIT FOR DRQ OR INTRQ. ORA A ;SET FLAGS. JP RDDONE ;DONE IF INTRQ. IN DDATA ;READ A DATA BYTE FROM DISK. MOV M,A ;PUT BYTE INTO MEMORY. INX H ;INCREMENT MEMORY POINTER. JMP RLOOP ;KEEP READING. RDDONE: IN DSTAT ;READ DISK STATUS. IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ALLOW AGAIN HERE. ENDIF ANI 9DH ;LOOK AT ERROR BITS. RZ ;RETURN IF NONE. CALL ERCHK ;CHECK FOR SEEK ERROR. LDA ERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ RRETRY ;TRY TO READ AGAIN. CALL RECOV ;CHECK FOR ABORT OR CONTINUE JMP READ ;IF NOT CNTL-C, TRY TO READ AGAIN. ; ;RECOV ;THIS ROUTINE IS CALLED BY ANY READ,WRITE,SEEK ;ROUTINE IF THE RETRY COUNT GOES TO 10. IF IT ;DOES,THIS ROUTINE CALLS CONIN FOR A KEY TO BE ;PUSHED. IF THE KEY IS A CNTL-C, THEN A WARMBOOT ;IS EXECUTED. IF ANY OTHER KEY IS PUSHED, THEN ;A RETURN IS MADE BACK TO THE CALLER AND THAT ;ROUTINE IS RETRIED FOR 10 MORE TIMES. ; RECOV: LXI H,ERRMSG ;POINT TO "ERROR" MESSAGE. CALL PMSG ;PRINT IT ON CONSOL. CALL CONIN ;CHECK FOR PUSHED KEY. CPI 03H ;IS IT A CNTL-C ? RNZ ;RETURN TO CALLER IF NOT. JMP WBOOT ;YES, DO WARMBOOT. ; ; ERCHK - CHECK FOR RECORD NOT FOUND ERROR. ; ERCHK: MOV D,A ;SAVE ERROR BITS IN D. ANI 10H ;IF RECORD NOT FOUND, JNZ CHKSK ;DO A CHECK ON SEEK. MOV A,D ;OTHERWISE RESTORE BITS ORA A ;SET FLAGS, RET ;AND RETURN. ;CHECK FOR SEEK TO CORRECT TRACK, ;AND CHANGE IF NECESSARY. CHKSK: MVI A,0C4H ;SEND COMMAND TO 1771 OUT DCOM ;TO READ ADDRESS. IN WAIT ;WAIT FOR DRQ OR INTRQ. IN DDATA ;READ THE TRACK ADDRESS. MOV B,A ;SAVE IN REGISTER B. CHKS2: IN WAIT ;WAIT FOR INTRQ. ORA A ;SET FLAGS. JP CHKS3 ;DONE WITH READ ADR OP. IN DDATA ;READ ANOTHER BYTE. JMP CHKS2 ;DO IT AGAIN. CHKS3: IN DSTAT ;READ DISK STATUS. ORA A ;SET FLAGS. JZ CHKS4 ;READ ADR OK IF 0. CALL HOME ;OTHERWISE, HOME FIRST. JMP CHKS5 CHKS4: MOV A,B ;UPDATE TRACK REGISTER. OUT TRACK CHKS5: LDA TRK ;GET REQUIRED TRACK NO. CALL SEEK ;MOVE THE HEAD TO IT. MOV A,D ;GET ERROR BITS. ORA A ;SET FLAGS. RET ;RETURN FROM ERCHK. ; ;DISK SET UP ROUTINE. ;THIS ROUTINE IS COMMON TO ;BOTH THE READ AND WRITE ;ROUTINES. ; DSKSET: STA ERCNT ;STORE IN ERROR CTR. MVI A,0D0H ;CAUSE INTERRUPT. OUT DCOM XTHL ;SOME XTHL ; DELAY IF INTRP ;IF INTERRUPTS ALLOWED, DI ;DISABLE THEM HERE. ENDIF LHLD DMAADD ;GET STARTING ADDR. LDA SECT ;GET SECTOR NUMBER. OUT SECTP ;SET SECTOR INTO 1771. CALL HDLD ;GET HEAD-LOAD BIT? RET ;RETURN TO CALLER ; ; WRITE THE SECTOR AT SECT, ON THE PRESENT TRACK. ; USE STARTING ADDRESS AT DMAADD. ; WRITE: MVI A,RTCNT ;GET RETRY COUNT. WRETRY: CALL DSKSET ;SET UP DISK. ADI 0A8H ;ADD CODE FOR WRITE. WRITE2: OUT DCOM WLOOP: IN WAIT ;WAIT FOR READY. ORA A ;SET FLAGS. JP WDONE ;HOP OUT WHEN DONE. MOV A,M ;GET BYTE FROM MEM. OUT DDATA ;WRITE ONTO DISK. INX H ;INCREMENT MEM PTR. JMP WLOOP ;KEEP WRITING. WDONE: IN DSTAT ;READ DISK STATUS. IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ENABLE AGAIN HERE. ENDIF ANI 0FDH ;LOOK AT THESE BITS. RZ ;RETURN IF NO ERR. CALL ERCHK ;CHECK/CORRECT SEEK ERR. LDA ERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ WRETRY ;TRY TO WRITE AGAIN. CALL RECOV ;CHECK FOR ABORT JMP WRITE ;RETRY WRITE AGAIN. ; ; PRINT THE MESSAGE AT H&L UNTIL A ZERO. ; PMSG: MOV A,M ;GET A CHARACTER. ORA A ;IF IT'S ZERO, RZ ;RETURN. MOV C,A ;OTHERWISE, CALL CONOT ;PRINT IT. INX H ;INCREMENT H&L, JMP PMSG ;AND GET ANOTHER. ; ; CBIOS MESSAGES ; ERRMSG: DB 'ERROR.',0 SMSG: DB 0DH,0AH,'Tarbell ' DB MSIZE/10+'0',MSIZE MOD 10 + '0' DB 'K CPM 2.2',0 ; ;LIST STATUS CHECK ROUTINE ; PRSTAT: IN LSTAT ;CHECK LIST STATUS PORT ANI LRBIT ;MASK AND CHECK READY BIT JMP CONST1 ;FINISH IN THE CONST ROUTINE. ; ; WRITE A CHARACTER ON LISTING DEVICE. ; LIST: IF LSTNUL ;IF NULLS OR PAGING, MVI A,0DH ;IF IT'S A CR, CMP C ;THEN HOP OUT TO JZ LINUL ;NULL ROUTINE. ENDIF IF LSTPAG ;IF PAGING MVI A,0AH ;GET A LINEFEED CMP C ;DOES IT MATCH? JZ LINUL3 ENDIF LTBSY: IN LSTAT ;READ LISTER STATUS. IF NOT DELTA ANI LRBIT ;LOOK AT READY BIT. ENDIF IF DELTA ANI 81H ;LOOK AT BITS 7 AND 0 XRI 81H ;BOTH MUST BE ONES TO PRINT JNZ LTBSY ;LOOP TILL ONES ENDIF IF NOT DELTA AND RDYLO JNZ LTBSY ;LOOP TILL LOW. ENDIF IF NOT DELTA AND RDYHI JZ LTBSY ;LOOP TILL HIGH. ENDIF MOV A,C ;GET DATA BYTE. OUT LDATA ;PRINT IT. RET ;RETURN FROM LIST. IF LSTNUL ;IF LIST NULLS LINUL: PUSH B ;SAVE B&C. MVI B,(LNULL AND 0FFH)+1 ;GET NULL COUNT LINUL1: CALL LTBSY ;PRINT (CR FIRST). MVI C,0 ;GET NULL CHAR. DCR B ;DECREMENT COUNTER. JNZ LINUL1 ;DO NEXT NULL. JMP LINUL2 ;EXIT THE ROUTINE. ENDIF IF LSTPAG ;IF LIST DEV. PAGING, LINUL3: PUSH B ;SAVE B,C PAIR LDA LFCNT ;GET LINE-FEED COUNT. INR A ;INCREMENT IT. STA LFCNT ;SAVE IT BACK. CPI LINCNT-(LINCNT/11) ;END OF PAGE? MVI B,1 ;SET UP FOR 1 LF. JNZ NOTEOP ;HOP IF NOT END. XRA A ;SET LF COUNT = 0. STA LFCNT MVI B,(LINCNT/11)+1 ;BETWEEN PAGES. NOTEOP: MVI C,0AH ;GET LINE-FEED CODE. LSTPA1: CALL LTBSY ;PRINT LINE-FEED. DCR B ;DECREMENT LF COUNTER. JNZ LSTPA1 ;DO NEXT LINE FEED? ENDIF IF LSTNUL OR LSTPAG ;IF NULLS OR PAGING, LINUL2: POP B ;RESTORE B&C. MOV A,C ;RESTORE A. RET ;RETURN FROM LIST. ENDIF ; ; PUNCH PAPER TAPE. ; PUNCH: RET ;RETURN FROM PUNCH. ; ; NORMALLY USED TO READ PAPER TAPE. ; READER: RET ;RETURN FROM READER. ;NOTE: AS THERE ARE ONLY SIX (6) SECTORS ;AVAILABLE FOR CBIOS ON THE SECOND SYSTEM TRACK (1), ;THE LAST ADDRESS BEFORE THIS POINT SHOULD BE NO ;GREATER THAN THE CBIOS STARTING ADDRESS + 037F (HEX). ;THIS WILL NORMALLY BE XD7F (HEX). ; ; BIOS SCRATCH AREA. ; TRK: DS 1 ;CURRENT TRACK NUMBER. SECT: DS 1 ;CURRENT SECTOR NUMBER. DMAADD: DS 2 ;DISK TRANSFER ADDRESS. ; ; THE NEXT SEVERAL BYTES, BETWEEN STARTZ AND ; ENDZ, ARE SET TO ZERO AT COLD BOOT TIME. ; STARTZ: ;START OF ZEROED AREA. DISKNO: DS 1 ;DISK NUMBER PDISK: DS 2 ;NEW DISK TO SELECT AND A ZERO BYTE ; SPECIAL FLAGS. HLSF: DS 1 ;HEAD-LOAD SELECT FLAG. LFCNT: DS 1 ;PAGING LINE-FEED COUNT. ; ; TRTAB - DISK TRACK TABLE - PRESENT POSITION OF ; HEADS FOR UP TO 4 DRIVES. ; TRTAB: DS 4 ENDZ: ;END OF ZEROED AREA. ERCNT: DS 1 ;ERROR COUNT FOR RETRIES. SERCNT: DS 1 ;SEEK RETRY COUNTER. TEMP: DS 1 ;TEMPORARY STORAGE. LATCH: DS 1 ;NEW CODE FOR LATCH. CLATCH: DS 1 ;CURRENT CODE IN LATCH. BEGDAT EQU $ DIRBUF: DS 128 ;DIRECTORY BUFFER ALV0: DS 31 CSV0: DS 16 ALV1: DS 31 CSV1: DS 16 ALV2: DS 31 CSV2: DS 16 ALV3: DS 31 CSV3: DS 16 ENDDAT EQU $ DATSIZ EQU $-BEGDAT ;TOTAL SIZE OF DISK PARM STORAGE. END