; CP/M BASIC INPUT/OUTPUT OPERATING SYSTEM (BIOS) ;( TARBELL ELECTRONICS) CVE MOD OF 9-5-81 ; 1.4 VERSION OF 7-20-79 ; THIS BIOS CAN ALSO BE USED WITH 1.3 VERSIONS BY ; CHANGING BDOS EQU FROM CBASE+3106H TO CBASE+3206H. ; (NOTE THAT CP/M VERSION 1.3 ONLY SUPPORTS 2 DRIVES, ; WHILE CP/M VERSION 1.4 WILL SUPPORT 4 DRIVES.) ; ; 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 32 ;MEMORY SIZE IN KBYTES. INTRP EQU FALSE ;TRUE IF INTERRUPTS ALLOWED. STD EQU TRUE ;TRUE IF STANDARD I/O OR SSM IO4. MSIO2 EQU FALSE ;TRUE IF MITS 2SIO. ISIO2 EQU FALSE ;TRUE IF IMSAI SIO-2. TUART EQU FALSE ;TRUE IF CROMEMCO TUART. VDM EQU FALSE ;TRUE IF PROC TECH VDM. FLASH EQU FALSE ;TRUE IF VG FLASHWRITER. VB1 EQU FALSE ;TRUE IF SSM VB1-B. OTHER EQU FALSE ;TRUE IF SOMETHING ELSE. SOLOS EQU FALSE ;TRUE IF PROC TECH SOLOS. BACKSP EQU TRUE ;AUTO-BACKSPACE FOR CRT'S. DUBSID EQU FALSE ;TRUE FOR DOUBLE SIDED DRIVES. 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 TRUE ;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 *** ******************************************************* VIDEO EQU VDM OR FLASH OR VB1 ;TRUE FOR ANY VIDEO. RDYLO EQU STD OR SOLOS OR OTHER ;STATUS READY WHEN LOW. RDYHI EQU NOT RDYLO IF VDM ;IF PROC TECH VDM1, SCREEN EQU 0CC00H ;SCREEN PLACE IN MEMORY. ENDSCR EQU (SCREEN+1024) SHR 8 ENDIF IF FLASH OR VB1 SCREEN EQU 0F800H ;SCREEN PLACE IS DIFFERENT. ENDIF IF FLASH ;IF VG FLASHWRITTER ENDSCR EQU (SCREEN+2048) SHR 8 ENDIF IF VB1 ;IF SSM VB1-B BOARD ENDSCR EQU (SCREEN+1024) SHR 8 ENDIF 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 ;IF IMSAI SIO-2, 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-17)*1024 ;BIAS FOR LARGER THAN 17K. CPMB EQU CBASE+2900H ;START OF CPM. BDOS EQU CBASE+3106H ;START OF BDOS 1.4. CPML EQU 1500H ;LENGTH OF CPM SYSTEM-BIOS. NSECTS EQU CPML/128 ;NUMBER OF SECTORS IN IT. IF DUBSID ;IF DOUBLE-SIDED, ORG CPMB+083BH ;DISK PARAMETER BLOCK. DB 7FH ;DIRMAX. DB 04H ;BLKSHF. DB 0FH ;BLKMSK. ; ORG CPMB+1072H ;CP/M PATCH. CALL CPMB+0AF4H NOP!NOP!NOP MOV A,C LXI H,CPMB+14F8H ENDIF ORG CPMB+1500H ;START OF BIOS. ; ; 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. ; THESE ENTRY POINTS ADDED BY TARBELL ELECTRONICS. 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. ; ; 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, NOP!NOP!NOP!NOP ;LEAVE SPACE FOR INIT. NOP!NOP!NOP!NOP;NO INIT REQ FOR IO4, MUST BE DONE ON BOARD NOP!NOP!NOP!NOP NOP!NOP!NOP!NOP 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 ;IF IMSAI SIO2, 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. 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. IF VIDEO ;IF ANY VIDEO BOARD, CALL CLEAR ;CLEAR SCREEN. ENDIF IF VDM ;IF PROC TECH VDM, XRA A ;INITIALIZE. OUT 0C8H ENDIF IN CDATA ;CLEAR CONSOLE STATUS. LXI H,SMSG ;PRINT OPENING MESSAGE. CALL PMSG CALL CONIN ;READ # OF DISKS. MOV C,A ;ECHO THE CHAR. CALL CONOT ANI 7 ;LOOK AT 3 LSB'S. STA NODSKS ;SAVE IT. GOCPM: LDA DISKNO ;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 DISKNO ;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 DISKNO CALL SETUP ;SET UP JUMPS. JMP GOCPM ;GO BACK TO CPM. ; RDERR: LXI H,BTMSG ;GET ADDRESS OF "BOOT ERROR". CALL PMSG ;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. 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. IF BACKSP ;IF BACKSPACE ACTIVATED, CPI 7FH ;IS IT A RUBOUT? RNZ ;RETURN IF NOT. MVI A,0FFH ;SET NO PRINT FLAG. STA CONOTF MVI A,7FH ;RESTORE RUBOUT. ENDIF RET ;RETURN FROM CONIN. ; ; WRITE A CHARACTER TO THE CONSOLE DEVICE. ; CONOT: IF BACKSP ;IF BACKSPACE ACTIVATED, MOV A,C ;GET CHARACTER. CPI 7FH ;IS IT A RUBOUT? RZ ;IF SO, DON'T PRINT IT. LDA CONOTF ;GET NO PRINT FLAG. ORA A ;SET CPU FLAGS. JZ CONOTA ;NOT SET, SO PRINT. XRA A ;RESET THE FLAG STA CONOTF ;TO ZERO. MVI C,8 ;PRINT BACKSPACE. CALL CONOTA MVI C,20H ;PRINT SPACE. CALL CONOTA MVI C,8 ;ANOTHER BACKSPACE. CONOTA: ENDIF IF CONUL AND NOT VIDEO ;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 VIDEO AND NOT SOLOS IN CSTAT ;READ CONSOLE STATUS. ANI CPTR ;IF NOT READY, ENDIF IF RDYLO AND NOT VIDEO AND NOT SOLOS JNZ CONOT1 ;LOOP UNTIL LOW. ENDIF IF RDYHI AND NOT VIDEO ;IF READY WHEN HIGH, JZ CONOT1 ;LOOP UNTIL HIGH. ENDIF IF NOT VIDEO AND NOT SOLOS MOV A,C ;GET CHARACTER. OUT CDATA ;PRINT IT. RET ;RETURN. ENDIF IF CONUL AND NOT VIDEO 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 IF VIDEO ;IF ANY VIDEO BOARD, ; ; ; VIDEO DRIVER FOR VB1-B BOARD OR ; VDM 1 BOARD ; ; ; WRITTEN BY G.W.MULCHIN ; 9-16-78 ; ; LINES15 EQU SCREEN+0040H;VIDEO+15 LINES SCRLCNT EQU 960 ;LINES TO SCROLL SCRNTOP EQU SCREEN+SCRLCNT ; BKSP EQU 08H ;BACKSPACE EQUATE LF EQU 0AH ;LINEFEED EQUATE CR EQU 0DH ;CARRIAGE RET EQUATE FF EQU 0CH ;FORM-FEED EQUATE. ; NORMALLY PART OF STORAGE AREA. ; MOV A,C ;GET THE CHAR INTO REG A PUSH H ;SAVE REGISTERS PUSH D PUSH B PUSH PSW ;CHAR. IS IN REG A CALL VIDPRO ;DO VIDEO ROUTINE POP PSW ;RESTORE REGISTERS POP B ; POP D ; POP H ; RET ;BACK TO CALLER VIDPRO: LHLD VDMP ;GET SCREEN POSITION POINTER CPI CR ;IS IT CARRIAGE RETURN? JZ CARRET CPI LF ;IS IT LINEFEED? JZ LFNOT ;PUT IN A BLANK CPI BKSP ;IS IT A RUBOUT? JZ BS CPI FF ;IS IT CNTL-L? JZ CLEAR MOV M,A ;IT HAS TO BE DATA UPDATE: INX H ;UPDATE POSITION GONE: MVI M,0A0H ;PUT CURSOR ON SCREEN JMP MAXLIN ;CHECK FOR LINE > 64 LFNOT: MVI M,' ' ;PUT IN A SPACE JMP UPDATE ;GET OUT NOW BS: MVI M,' ' LHLD VDMP ;GET CURRENT POSITION DCX H SHLD VDMP ;SAVE CURSOR POSITION JMP GONE CARRET: MVI M,' ' ;CHAR. IS A CARRIAGE RET. MOV A,L ;UPDATE NEXT POSITION ANI 0C0H ADI 40H ;SET UP FOR NEW LINE MOV L,A ;ADDRESS OF NEW LINE MVI A,0 ADC H MOV H,A MAXLIN: SHLD VDMP ;SAVE POINTER FOR NEXT CHAR. MVI A,7FH ANA L RNZ ;EXIT BACK TO MAIN PROGRAM MVI M,' ' LXI H,SCRNTOP SHLD VDMP LXI H,LINES15 ;15 LINES OF SCREEN DATA LXI D,SCREEN ;TOP OF SCREEN. SET UP LXI B,SCRLCNT ; TO SCROLL 15 LINES SCROLL: MOV A,M ;START SCROLLLING UP STAX D ;STUFF REG A BY WAY OF D,E INX H INX D DCX B XRA A CMP B ;15 LINES YET? JNZ SCROLL CMP C JNZ SCROLL ;NOT DONE YET! LXI H,SCRNTOP BLANK: MVI M,' ' ;PUT BLANK ON SCREEN INX H ;BLANK ENTIRE DATA LINE MOV A,L ANI 3FH JNZ BLANK LXI H,SCRNTOP MVI M,0A0H ;STUFF CURSOR BACK RET ;ALL DONE......!!!! CLEAR: LXI H,SCREEN ;CLEAR SCREEN MVI A,ENDSCR ;THIS IS END CHECK CLERA: CMP H ;IS IT END YET? JZ FINISH MVI M,' ' INX H JMP CLERA ;GO BACK IF NOT DONE FINISH: LXI H,SCRNTOP MVI M,0A0H ;STUFF CURSOR BACK AGAIN SHLD VDMP RET ;ALL DONE! ENDIF ;END OF VDM DRIVER. ; ; SELECT DISK NUMBER ACCORDING TO REGISTER C. ; SELDSK: MOV A,C ;GET NEW DISK NUMBER. ANI 3 ;ONLY LOOK AT 2 LSB'S. LXI H,DISKNO ;GET ADR OF OLD DISK NO. CMP M ;NEW = OLD? RZ ;IF SO, RETURN. PUSH A ;SAVE DISK NUMBER. LDA NODSKS ;GET NUMBER OF DISKS. DCR A ;IF MORE THAN ONE DISK, JNZ SELMOR ;TAKE CARE OF IT. LXI H,MNTMSG ;GET ADR OF MOUNT MESSAGE. CALL PMSG ;PRINT "MOUNT ". POP A ;GET DISK NUMBER. STA DISKNO ;UPDATE OLD WITH NEW. ADI 'A' ;ADD ASCII FOR 'A'. MOV C,A ;PUT INTO C. CALL CONOT ;PRINT IT. CALL CONIN ;READ A CARRIAGE RETURN. XRA A ;SET A=0 FOR NO ERRO IND. RET ;RETURN FROM SELDSK. SELMOR: POP A ;MAKE STACK RIGHT. 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. 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. LXI H,TRTAB ;GET ADDRESS OF TRACK TABLE. 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. 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 ;SAVE IN C. MOV A,C ;DELAY. CMP B ;SAME AS NEW TRACK NO.? MOV A,B ;RESTORE A FROM B. 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 AND FLAGS MVI A,0D0H ;DELAY COUNT = 208 BUSY1: DCR A ;DECREASE COUNT JNZ BUSY1 ;LOOP TILL DONE POP PSW ;RESTORE ACCUM AND FLAGS IF NOT PERSCI ;IF NOT PERSCI DRIVE, 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, PUSH H ;SAVE H&L. LXI H,SECNT ;GET ADR OF SEEK ERR CTR. INR M ;ONE MORE SEEK ERROR. POP H ;RESTORE H&L. LDA SERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ SRETRY ;RETRY SEEK. POP B ;RESTORE B&C. LXI H,SKMSG ;PRINT "SEEK ". IN DSTAT ;READ DISK STATUS. ANI 91H ;LOOK AT ERROR BITS. MOV D,A ;PUT IN REG D. JMP ERMSG ;DO COMMON ERR MESSAGES. ENDIF ; ; SET DISK SECTOR NUMBER. ; SETSEC: MOV A,C ;GET SECTOR NUMBER. STA SECT ;PUT AT SECT # ADDRESS. RET ;RETURN FROM SETSEC. ; ; 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: STA ERCNT ;STORE IN ERROR CTR. LHLD DMAADD ;GET STARTING ADR. MVI A,0D0H ;CAUSE INTERRUPT. OUT DCOM XTHL ;SOME DELAY. XTHL IF INTRP ;IF INTERRUPTS ALLOWED, DI ;DISABLE THEM HERE. ENDIF READ3: LDA SECT ;GET SECTOR NUMBER. OUT SECTP ;SET SECTOR INTO 1771. CALL HDLD ;GET HEAD-LOAD BIT? 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. LXI H,RECNT ;GET RD ERR COUNT ADDR. INR M ;ONE MORE ERROR. LDA ERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ RRETRY ;TRY TO READ AGAIN. LXI H,RDMSG ;PRINT "READ ". ERMSG: CALL PMSG ;PRINT ORIGIN MESSAGE. ERMSG1: IF NOT VIDEO ;NEED MORE ROOM? MOV A,D ;GET ERROR BITS. ANI 80H ;IF BIT 7 HIGH, LXI H,NRMSG ;"NOT READY". CNZ PMSG MOV A,D ;GET ERROR BITS. ANI 10H ;IF BIT 4 IS HIGH, LXI H,RNMSG ;PRINT "RECORD NOT FOUND" CNZ PMSG MOV A,D ;GET ERROR BITS. ANI 8H ;IF BIT 3 IS HIGH, LXI H,CRCMSG ;PRINT "CRC ERROR". CNZ PMSG MOV A,D ;GET ERROR BITS. ANI 4H ;IF BIT 2 IS HIGH, LXI H,LDMSG ;PRINT "LOST DATA". CNZ PMSG MOV A,D ;GET ERROR BITS. ANI 1 ;IF BIT 1 IS HIGH, LXI H,BSYMSG ;PRINT "BUSY". CNZ PMSG ENDIF LXI H,ERRMSG ;PRINT "ERROR." CALL PMSG MVI A,1 ;SET FOR PERM ERR MSG. ORA A ;SET FLAGS. RET ; ; 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. ; ; WRITE THE SECTOR AT SECT, ON THE PRESENT TRACK. ; USE STARTING ADDRESS AT DMAADD. ; WRITE: MVI A,RTCNT ;GET RETRY COUNT. WRETRY: STA ERCNT ;STORE IN ERROR COUNTER. LHLD DMAADD ;GET STARTING ADR. MVI A,0D0H ;STATUS INTERUPT FOR 1771. OUT DCOM ;COMMAND 1771. XTHL ;WAIT FOR STATUS. XTHL ;CHANGE IT BACK. IF INTRP ;IF INTERRPUTS ALLOWED, DI ;DISABLE THEM HERE. ENDIF WRITE3: LDA SECT ;GET SECTOR NUMBER. OUT SECTP ;SET THE SECTOR INTO 1771. CALL HDLD ;GET HEAD LOAD BIT? 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. LXI H,WECNT ;GET ADR OF WRITE ERR CTR. INR M ;ONE MORE WRITE ERROR. LDA ERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ WRETRY ;TRY TO WRITE AGAIN. LXI H,WTMSG ;PRINT "WRITE ". IF NOT VIDEO ;NEED MORE ROOM? CALL PMSG MOV A,D ;GET ERROR BITS. ANI 40H ;LOOK AT BIT 6. LXI H,WPMSG ;PRINT "PROTECT ". CNZ PMSG MOV A,D ;GET ERROR BITS. ANI 20H ;LOOK AT BIT 5. LXI H,WFMSG ;PRINT "FAULT ". CNZ PMSG JMP ERMSG1 ;DO COMMON MESSAGES. ENDIF IF VIDEO ;WE NEED A RETURN. JMP ERMSG ENDIF ; ; 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 ; IF NOT VIDEO ;NEED MORE ROOM? NRMSG: DB 'NOT READY ',0 RNMSG: DB 'RECORD NOT FOUND ',0 CRCMSG: DB 'CRC ',0 LDMSG: DB 'LOST DATA ',0 BSYMSG: DB 'BUSY ',0 WPMSG: DB 'PROTECT ',0 WFMSG: DB 'FAULT ',0 ENDIF BTMSG: DB 'BOOT ' ERRMSG: DB 'ERROR.',0 RDMSG: DB 0DH,0AH,'READ ',0 WTMSG: DB 0DH,0AH,'WRITE ',0 SKMSG: DB 0DH,0AH,'SEEK ',0 MNTMSG: DB 0DH,0AH,'MOUNT ',0 SMSG: DB 0DH,0AH,'CVE IO4 ' DB MSIZE/10+'0',MSIZE MOD 10 + '0' DB 'K CPM V1.4 OF 7-20-79/9-5-81' DB 0DH,0AH IF STD ;IF STANDARD I/O, DB 'STANDARD ' ENDIF IF MSIO2 ;IF MITS 2SIO, DB '2SIO ' ENDIF IF ISIO2 ;IF IMSAI SIO-2, DB 'SIO-2 ' ENDIF IF TUART ;IF TUART, DB 'TUART ' ENDIF IF SOLOS ;IF PROC TECH SOLOS, DB 'SOLOS ' ENDIF IF VDM ;IF PROC TECH VDM, DB 'VDM ' ENDIF IF FLASH ;IF VG FLASHWRITER, DB 'FLASHWRITER ' ENDIF IF VB1 ;IF SSM VB1-B, DB 'VB1 ' ENDIF IF DUBSID ;IF DOUBLE-SIDED, DB 'DOUBLE-SIDED ' ENDIF IF DUAL ;IF DUAL DRIVE, DB 'DUAL ' ENDIF IF PERSCI ;IF PERSCI DRIVE, DB 'PERSCI ' ENDIF DB 'VERSION.' DB 0DH,0AH,'HOW MANY DISKS? ',0 ; ; 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. ANI LRBIT ;LOOK AT READY BIT. IF RDYLO ;IF READY WHEN LOW, JNZ LTBSY ;LOOP TILL LOW. ENDIF IF RDYHI ;IF READY WHEN HIGH, 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 NINE 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 + 047F (HEX). ;THIS WILL NORMALLY BE XE7F (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 (TO CP/M). ; ; ERROR COUNTS. THESE LOCATIONS KEEP TRACK OF THE ; NUMBER OF ERRRS THAT OCCUR DURING READ, WRITE, ; OR SEEK OPERATIONS. THEY ARE INITIALIZED ONLY ; WHEN A COLD-START IS PERFORMED BY THE BOOOTSTRAP. ; RECNT: DS 1 ;READ ERROR COUNT. WECNT: DS 1 ;WRITE ERROR COUNT. SECNT: DS 1 ;SEEK ERROR COUNT. ; SPECIAL FLAGS. CONOTF: DS 1 ;NO-PRINT FLAG (WHEN FF). 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. ; ; VDM SCRATCH AREA. ; IF VIDEO ;IF VIDEO BOARD IN, VDMP: DS 2 ;VIDEO CURSOR POSITION. ENDIF NODSKS: DS 1 ;NUMBER OF DISKS. 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. END