; BASIC INPUT/OUTPUT OPERATING SYSTEM ; TARBELL ELECTRONICS ; 4-DRIVE VERSION OF 5-24-'78 ; (NOTE THAT CP/M VERSION 1.3 ONLY SUPPORTS 2 DRIVES, ; WHILE CP/M VERSION 1.4 WILL SUPPORT 4 DRIVES.) ; ; JPL VERSION 01-29-79 ; ; THIS MODULE CONTAINS ALL THE INPUT/OUTPUT ; ROUTINES FOR THE CP/M SYSTEM, INCLUDING ; THE DISK ROUTINES. ; MSIZE EQU 48 ;MEMORY SIZE. ; 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. Z80 EQU FALSE ;TRUE IF Z-80 PROCESSOR INTRP EQU FALSE ;TRUE IF INTERRUPTS ALLOWED. STD EQU FALSE ;TRUE IF STANDARD I/O. MSIO2 EQU FALSE ;TRUE IF MITS 2SIO. ISIO2 EQU FALSE ;TRUE IF IMSAI SIO-2. TUART EQU TRUE ;TRUE IF CROMEMCO TUART. VDM EQU FALSE ;TRUE IF PROC TECH VDM. SIO2 EQU MSIO2 OR ISIO2 OR TUART OTHER EQU FALSE ;TRUE IF SOMETHING ELSE. CSTAT EQU 0 ;CONSOLE STATUS PORT. CCOM EQU 0 ;CONSOLE COMMAND PORT. CDATA EQU 1 ;CONSOLE DATA PORT. CADR EQU 3 ;INTERRUPT ADDRESS CPAR EQU 4 ;PARALLEL DATA PORT 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. CINT EQU 00100000B ;INTERRUPT PENDING STATUS ENDIF IF OTHER ;IF SOMETHING ELSE, CKBR EQU 00000010B ;KEYBOARD READY BIT. CPTR EQU 10000000B ;PRINTER READY BIT. ENDIF CNULL EQU 3 ;CONSOLE NULL COUNT. LSTAT EQU CSTAT ;LIST STATUS PORT. LCOM EQU 2 ;LIST COMMAND PORT. LDATA EQU CDATA ;LIST DATA PORT. LRBIT EQU CPTR ;LIST READY BIT. LNULL EQU 2 ;LIST NULL COUNT. DUAL EQU TRUE ;TRUE IF DUAL DRIVE. FAST EQU TRUE ;TRUE IF FAST SEEK. DISK EQU 0F8H ;DISK BASE ADDRESS. 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. LAST EQU MSIZE*1024 FIRST EQU LAST-1536 LASTM EQU FIRST+4A0H LISTR EQU LASTM+3 VIOMOD EQU LISTR+3 TBUFF EQU 80H ORG FIRST CBASE EQU (MSIZE-17)*1024 ;BIAS FOR LARGER THAN 17K. CPMB EQU CBASE+2900H ;START OF CPM. BDOS EQU CBASE+3106H ;START OF BDOS. CPML EQU $-CPMB ;LENGTH OF CPM SYSTEM-BIOS. NSECTS EQU CPML/128 ;NUMBER OF SECTORS IN IT. ; ; 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. READN: JMP READ ;READ SELECTED SECTOR. WRITEN: JMP WRITE ;WRITE SELECTED SECTOR. ; THESE ENTRY POINTS ADDED BY TARBELL ELECTRONICS. JMP READN ;READ WITH NO HEAD LOAD. JMP WRITEN ;WRITE WITH NO HEAD LOAD. ; ; 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. ; IOBYTE CONSOLE=CRT, READER=TTY, PUNCH=TTY, LIST=TTY MVI A,01H ;SET IOBYTE STA 3 ;STORE IOBYTE IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ENABLE THEM HERE. ENDIF IF VDM ;IF PROC TECH VDM, CALL CLR ;CLEAR VDM SCREEN. ENDIF IF STD ;IF STANDARD I/O, NOP!NOP!NOP!NOP ;LEAVE SPACE FOR INIT. NOP!NOP!NOP!NOP 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 MVI A,40H OUT CCOM MVI A,0CEH OUT CCOM MVI A,37H OUT CCOM ENDIF IF TUART ;IF CROMEMCO TUART, MVI A,9 ;SET A = 1. OUT 02H ;RESET A OUT 52H ;RESET DEVICE B MVI A,0 ;SET A = 0 OUT 03H ;MASK A SET OUT 53H ;MASK B SET MVI A,84H ;300, 1 STOP BIT OUT 0 ENDIF CALL GOCPM ; MVI C,0DH ;RESET THINGS CALL 5 MVI C,15 ;OPEN FILE CALL FDOS XRA A STA TFCB+32 ;SET REC COUNT=0 ; LXI H,LASTM ;ADDR TO LOAD SYSTEM PUSH H ;SAVE ORIGIN OF LOAD MLOOP: MVI C,20 ;READ REC FROM DISK CALL FDOS ORA A JNZ CLOSE ;READ OK ? POP H CALL SAVIT ;SAVE CHAR PUSH H JMP MLOOP ; CLOSE: POP H MVI C,16 ;CLOSE FILE CALL FDOS CALL VIOMOD ;INITIALIZE IMSAI DTV BOARD LXI H,SMSG ;PRINT OPENING MESSAGE. CALL PMSG LDA DISKNO ;FETCH DISK NUMBER MOV C,A ;PASS TO CCP JMP CPMB ; FDOS: LXI D,TFCB JMP 5 ; SAVIT: LXI D,TBUFF ;GET ADDR OF BUFFER SAV1: LDAX D ;PICK UP CHAR FROM DISK BUFFER MOV M,A ;SAVE IT AFTER CBIOS INX D INX H MOV A,D CPI 1 ;80 BYTES XFERED ? JNZ SAV1 ; RET ; TFCB: DB 0,'SYSTEM$$COM',0,0,0,0,0 DS 17 ; GOCPM: 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 ; ; WARM-BOOT: READ ALL OF CPM BACK IN ; EXCEPT BIOS, THEN JUMP TO CCP. ; WBOOT: LXI SP,80H ;SET STACK POINTER. ; DO NOT RESET IOBYTE, USE DEFAULT. CONSOLE=CRT ; XRA A ;SET IOBYTE ; STA 3 ;ALL=TTY IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ALLOW THEM HERE. 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: MOV A,B ;GO TO TRACK IN B. CALL SEEK JNZ RDERR ;IF ERROR, PRINT MESSAGE. MOV A,C ;READ STARTING AT SECTOR IN C. CALL READ1 RBLK1 JNZ RDERR ;IF ERROR, PRINT MESSAGE. DCR D ;DECREMENT SECTOR COUNT. JZ ALDON ;ALL DONE WHEN ZERO. INR C ;INCREMENT SECTOR NUMBER. MOV A,C ;IF SECTOR NUMBER CPI 27 ;IS NOT 27, JC RBLK2 ;HOP OUT OF LOOP. 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 GOCPM LDA DISKNO ;FETCH DISK NUMBER MOV C,A ;PASS TO CCP JMP CPMB ; RBLK2: CALL READ2 ;READ ANOTHER TRACK. JMP RBLK1 ; 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 STD ;IF STANDARD I/O, RNZ ;NOT READY WHEN NOT 0. ENDIF IF SIO2 ;IF MITS OR IMSAI OR TUART, RZ ;NOT READY WHEN ZERO. ENDIF IF OTHER ;IF SOMETHING ELSE, RNZ ;IT MIGHT BE THIS. ENDIF CMA ;IF READY A=FF. RET ;RETURN FROM CONST. ; ; READ A CHARACTER FROM CONSOLE OR PARALLEL KEYBOARD. ; CONIN: IF TUART ;IF TUART MVI A,04H ;GET INT. MASK FOR SENS OUT CADR ;OUTPUT IT ENDIF IN CSTAT ;READ CONSOLE STATUS. IF TUART ;IF TUART STA STATIN ;SAVE INPUT STATUS ANI CINT ;ISOLATE INT. PENDING BIT JZ CONIN1 ;IF NOT, GO CHECK TTY STATUS IN CADR ;GET INT. ADDR. IN CPAR ;GET DATA FROM PARALLEL PORT ANI 7FH ;MAKE MOST SIG. BIT=0. RET CONIN1: LDA STATIN ;RESTORE STATUS ENDIF ANI CKBR ;IF NOT READY, IF STD ;IF STANDARD I/O, JNZ CONIN ;READY WHEN LOW. ENDIF IF SIO2 ;IF MITS OR IMSAI OR TUART, JZ CONIN ;READY WHEN HIGH. ENDIF IF OTHER ;IF SOMETHING ELSE, JNZ CONIN ;IT MIGHT BE THIS. ENDIF IN CDATA ;READ A CHARACTER. ANI 7FH ;MAKE MOST SIG. BIT = 0. RET ; ; WRITE A CHARACTER TO THE CONSOLE DEVICE. ; IF NOT VDM ;IF NOT PROC TECH VDM, CONOT: LDA 3 ;FETCH IOBYTE ANI 1 ;CRT ? JZ CONTTY CALL LASTM RET ; CONTTY: MVI A,0DH ;IF IT'S A CR, CMP C ;THEN HOP OUT JZ CONUL ;TO NULL ROUTINE. CONOT1: IN CSTAT ;READ CONSOLE STATUS. ANI CPTR ;IF NOT READY, ENDIF IF STD AND NOT VDM ;IF STANDARD I/O, JNZ CONOT1 ;READY WHEN LOW. ENDIF IF SIO2 ;IF MITS OR IMSAI, JZ CONOT1 ;READY WHEN HIGH. ENDIF IF NOT VDM ;IF NOT PROC TECH VDM, MOV A,C ;GET CHARACTER. OUT CDATA ;PRINT IT. RET ;RETURN. CONUL: PUSH B ;SAVE B&C. MVI B,CNULL ;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 VDM ;IF PROC TECH VDM, ; VDM DRIVER FOR CBIOS. ; 9-24-77 VERSION ; VDMB EQU 0CC00H VDMP EQU 0CCH VDMD EQU 0C8H CONOT: MOV A,C ;PUT CHAR IN A. SHLD HLSAV ;SAVE H&L. LXI H,0 ;CLEAR H&L. DAD SP ;HL=SP. LXI SP,VSTACK ;SET STACK PTR. PUSH H ;SAVE OLD SP. PUSH D ;SAVE D&E. PUSH B ;SAVE B&C. PUSH PSW ;SAVE A&PSW. CALL SCREEN ;WRITE ON SCREEN. POP PSW ;RESTORE A&PSW. POP B ;RESTORE B&C. POP D ;RESTORE D&E. POP H ;RESTORE H&L. SPHL ;RESTORE SP. LHLD HLSAV ;RESTORE H&L. RET ;RETURN FROM CONOT. ; ; SCREEN: MOV B,A ;GET CHARACTER. MOV A,B ANI 7FH ;NOT DELETE. CPI 0DH ;IF IT'S CR, JZ CHOT2 ;TAKE CARE OF IT. CPI 20H ;CTL CHAR? RC ;DON'T DISPLAY. JMP CHOUT ;GO TO CHOUT. ; ; CLEAR SCREEN & INIT CURSOR. ; CLR: LXI H,VDMB ;GET VDM MEM ADR. MOV A,H ;FIGURE VDM MEM TOP. ADI 4 CLR2: MVI M,' ' ;PUT SPACE IN MEMORY. INX H ;INCREMENT POINTER. CMP H ;AT TOP YET? JNZ CLR2 ;KEEP GOING IF NOT. XRA A ;SET A = 0. STA BOSL ;BEG SCRN LINE = 0. STA BOTL ;BEG TEXT LINE = 0. STA CCP ;CURSOR PTR = 0. CMA ;SET A = FF. STA CURF ;SET CURSOR ON. MVI A,15 ;SET CURSOR AT BOTTOM. STA CLN CALL VDMOT ;SET VDM UP. RET ;RETURN FROM CLR. ; ; OUTPUT BOSL & BOTL TO VDM. ; VDMOT: LDA BOSL ;INITIALIZE VDM. RLC ;SHIFT LEFT 4. RLC RLC RLC LXI H,BOTL ORA M OUT VDMD ;OUT TO VDM PORT. RET ;RETURN FROM VDMOT. ; ; STORE CHAR IN VDM MEMORY. ; CHOUT: MOV C,A ;SAVE CHARACTER. LDA CCP ;GET CURSOR PTR. MOV B,A ;PUT IN B. LDA CLN ;GET LINE NUMBER. CALL CLNA ;CONVERT TO ADR. MOV M,C ;PUT CHAR ON SCREEN. LDA CCP ;ADVANCE CURSOR. INR A CPI 64 ;WRAP AROUND? JNZ CHOT1 CHOT2: LDA CCP ;GET CURSOR POS. MOV B,A LDA CLN ;GET LINE NUMBER. CALL CCUR ;CLEAR CURSOR. CALL SCRL ;SCROLL UP. SUB A ;CURSOR TO LEFT MARGIN. CHOT1: STA CCP MOV B,A LDA CLN JMP SCUR ;SET CURSOR ON/OFF. ; ; SCRL: LXI H,BOTL ;SAVE BEG. TEXT LINE. PUSH H MOV A,M INR M SUB M LXI B,0 CALL CLNA ;CONVERT LINE NO. LXI B,2040H SCRL2: MOV M,B ;CLEAR BOTTOM LINE. INR L DCR C JNZ SCRL2 POP H MOV A,M ANI 0FH MOV M,A JMP VDMOT ; ; CONVERT LINE NUMBER IN REG A AND CHR ; POSITION IN REG B TO ADDRESS IN H&L. ; CLNA: MOV L,A LDA BOTL ADD L RRC RRC MOV L,A ANI 3 ADI VDMP MOV H,A MOV A,L ANI 0C0H ADD B MOV L,A RET ; ; SCUR: ANI 0FH STA CLN CALL CLNA MOV A,B STA CCP LDA CURF ORA A MOV A,M JZ CCUR2 ORI 80H MOV M,A RET CCUR2: ANI 7FH MOV M,A RET ; ; CCUR: CALL CLNA MOV A,M ANI 7FH MOV M,A RET ; ; CLN: DS 1 CCP: DS 1 CURF: DS 1 BOSL: DS 1 BOTL: DS 1 HLSAV: DS 32 VSTACK: DS 1 ENDIF ;END OF VDM DRIVER. ; ; MOVE DISK TO TRACK ZERO. ; HOME: MVI A,RTCNT ;GET RETRY COUNT. HRETRY: STA ERCNT ;STORE IN ERROR CTR. MVI A,0D0H ;CLEAR ANY PENDING COMMAND. OUT DCOM LDA DISKNO ;GET DISK NUMBER. MOV E,A ;PUT IN D&E. XRA A MOV D,A LXI H,TRTAB ;GET ADR OF TRK TABLE. IF NOT DUAL ;IF NOT A DUAL DRIVE. DAD D ;ADD DISK NUMBER. ENDIF MOV M,A ;PUT ZERO INTO TABLE. HOME1: IN DSTAT ;READ DISK STATUS. RRC ;LOOK AT LSB. JC HOME1 ;WAIT FOR NOT BUSY. IF FAST ;IF FAST SEEK, LDA NODSKS ;GET NUMBER OF DISKS. DCR A ;IF ONLY ONE DISK, JZ HOME2 ;SKIP OVER NEXT PART. LDA DISKNO ;GET DISK NUMBER. RLC!RLC!RLC!RLC ;SHIFT LEFT 4 BITS. ANI 30H ;LOOK AT BITS 4 AND 5. HOME2: CMA ;INVERT. MOV E,A ;SAVE IT. ANI 0B2H ;SET PERSCI OUT DCONT ;RESTORE LINE. HLOOP: IN DSTAT ;LOOK AT STATUS ANI 4 ;BIT 2 (TRACK 0) JZ HLOOP ;AND WAIT TILL 1. MOV A,E ;RESTORE OLD BITS ANI 0F2H ;IN LATCH AND CLEAR OUT DCONT ;RESTORE LINE. ENDIF MVI A,2 ;10 MS STEP RATE. OUT DCOM ;ISSUE HOME COMMAND. IN WAIT ;WAIT FOR INTRQ. ORA A ;SET FLAGS. JM HERR ;ERROR IF DRQ. IN DSTAT ;READ DISK STATUS. MOV D,A ;SAVE IN REGISTER D. ANI 4 ;LOOK AT BIT 2. JZ HERR ;ERROR IF NOT TRK 0. MOV A,D ;GET STATUS BACK. ANI 91H ;MASK NON-ERROR BITS. RZ ;RETURN IF NO ERROR. HERR: LDA HECNT ;GET TOTAL ERROR COUNT. INR A ;ADD ONE. STA HECNT ;SAVE IT BACK. LDA ERCNT ;GET THIS ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ HRETRY ;TRY TO HOME AGAIN. LXI H,HEMSG ;PRINT "HOME ". MOV A,D ;MASK NON-ERROR BITS. ANI 91H MOV D,A JMP ERMSG ;DO COMMON ERROR MSGS. ; ; 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. DSK1: OUT DCONT ;SET THE LATCH WITH CODE. XRA A ;SET A = 0. RET ;RETURN FROM SELDSK. ; ; SET TRACK NUMBER TO WHATEVER IS IN REGISTER C. ; ALSO PERFORM MOVE TO THE CORRECT TRACK (SEEK). ; SETTRK: MOV A,C ;GET NEW TRACK NUMBER. STA TRK ;UPDATE OLD WITH NEW. CALL SEEK ;MOVE TO NEW TRACK. RET ;RETURN FROM SETTRK ROUTINE. ; ; 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. ; ; READ A SECTOR WITHOUT LOADING HEAD FIRST. ; READ2: OUT SECTP ;SET SECTOR NUMBER INTO 1771. MVI A,88H ;GET CODE FOR READ W/O HLD. JMP READE ;READ A SECTOR. ; ; 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 IN DSTAT ;READ STATUS. ANI 20H ;LOOK AT HLD BIT. LDA SECT ;GET SECTOR NUMBER. READ1: OUT SECTP ;SET SECTOR INTO 1771. MVI A,8CH ;READ WITH HEAD LOAD JZ READE ;HEAD NOT LOADED. MVI A,88H ;CODE FOR READ W/O HD LD. READE: PUSH B ;SAVE BC REGISTER PAIR MVI C,DDATA ;PICK UP DA OF DATA REGISTER IN 1771 OUT DCOM ;SEND COMMAND TO 1771. RLOOP: IN WAIT ;WAIT FOR DRQ OR INTRQ. ORA A ;SET FLAGS. JP RDDONE ;DONE IF INTRQ. IF NOT Z80 ;IF 8080 PROCESSOR IN DDATA ;READ A DATA BYTE FROM DISK. MOV M,A ;PUT BYTE INTO MEMORY. INX H ;INCREMENT MEMORY POINTER. ENDIF ; IF Z80 ;IF Z-80 PROCESSOR DW 0A2EDH ;Z-80 'INI' COMMAND ENDIF JMP RLOOP ;KEEP READING. RDDONE: POP B ;RESTORE BC REGISTER PAIR 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. CHECK: 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 VDM ;IF NOT PROC TECH VDM, 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 PERMSG: 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 IN DSTAT ;GET 1771 STATUS. ANI 20H ;CHECK FOR HEAD LOAD. LDA SECT ;GET SECTOR NUMBER. WRITE1: OUT SECTP ;SET THE SECTOR INTO 1771. MVI A,0ACH ;SET UP 1771 FOR WRITE. JZ WRITE2 ;HEAD IS NOT LOADED. MVI A,0A8H ;CODE FOR WRITE W/O HD LD. WRITE2: PUSH B ;SAVE BC REGISTER PAIR MVI C,DDATA ;PICK UP DA OF DATA REGISTER IN 1771 OUT DCOM WLOOP: IN WAIT ;WAIT FOR READY. ORA A ;SET FLAGS. JP WDONE ;HOP OUT WHEN DONE. IF NOT Z80 ;IF 8080 PROCESSOR MOV A,M ;GET BYTE FROM MEM. OUT DDATA ;WRITE ONTO DISK. INX H ;INCREMENT MEM PTR. ENDIF ; IF Z80 ;IF Z-80 PROCESSOR DW 0A3EDH ;Z-80 'OUTI' COMMAND ENDIF ; JMP WLOOP ;KEEP WRITING. WDONE: POP B ;RESTORE BC REGISTER PAIR IN DSTAT ;READ DISK STATUS. IF INTRP ;IF INTERRUPTS ALLOWED, EI ;ENABLE AGAIN HERE. ENDIF ANI 0FDH ;LOOK AT THESE BITS. PROCER: 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. WERR0: LXI H,WTMSG ;PRINT "WRITE ". IF NOT VDM ;IF NOT PROC TECH VDM, 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 VDM ;IF PROC TECH VDM, JMP ERMSG ENDIF ; ; 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: IF NOT FAST ;IF NOT FAST SEEK, 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,12H ;SET FOR 10 MS STEP. ORI 4 ;VERIFY ON LAST TRACK. OUT DCOM ;ISSUE SEEK COMMAND. IN WAIT ;WAIT FOR INTRQ. IN DSTAT ;READ STATUS. ANI 91H ;LOOK AT BITS. JZ THERE ;OK IF ZERO. ENDIF IF FAST ;IF FAST SEEK, MVI A,40H ;IF CARRY = 1, JC SDIR ;STEP IN. MVI A,60H ;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 DISKNO ;GET DISK NUMBER. RLC!RLC!RLC!RLC ;SHIFT LEFT 4 BITS. ANI 30H ;LOOK AT BITS 4 AND 5. CMA ;INVERT. MOV B,A ;SAVE IN B. ANI 72H ;MAKE COMMAND TO OUT DCONT ;SWITCH WAIT FOR IN WAIT ;SEEK COMPLETE. MOV A,B ;RESTORE ORIG. BITS. ANI 0F2H ;SWITCH WAIT BACK. OUT DCONT XRA A ;MAKE GOOD RETURN. POP B ;RESTOREE B&C. RET ENDIF IF NOT FAST ;IF NOT FAST SEEK, 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 ERCNT ;GET ERROR COUNT. DCR A ;DECREMENT COUNT. JNZ SRETRY ;RETRY SEK. 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 ; ; 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 VDM ;IF NOT PROC TECH VDM, NRMSG: DB 'NOT RDY ',0 RNMSG: DB 'REC NOT FND ',0 CRCMSG: DB 'CRC ',0 LDMSG: DB 'LOST DATA ',0 BSYMSG: DB 'BUSY ',0 WPMSG: DB 'PROT ',0 WFMSG: DB 'FAULT ',0 ENDIF ERRMSG: DB 'ERR',0 RDMSG: DB 0DH,0AH,'READ ',0 WTMSG: DB 0DH,0AH,'WRITE ',0 BTMSG: DB 'BOOT ERR',0 SKMSG: DB 0DH,0AH,'SEEK ',0 HEMSG: DB 0DH,0AH,'HOME ',0 MNTMSG: DB 0DH,0AH,'MOUNT ',0 SMSG: DB 0DH,0AH DB 'NWC VER: 2.0 01-29-79' DB 0DH,0AH DB MSIZE/10+'0',MSIZE MOD 10 + '0','K ' 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 VDM ;IF PROC TECH VDM, DB 'VDM ' ENDIF IF FAST ;IF FAST SEEK, DB 'F SEEK ' ENDIF IF DUAL ;IF DUAL DRIVE, DB 'DUAL ' ENDIF IF Z80 DB 'Z-80 ' ENDIF IF NOT Z80 DB '8080 ' ENDIF DB 'VER' DB 0DH,0AH,0 ; ; WRITE A CHARACTER ON LISTING DEVICE. ; LIST: CALL LISTR ;CODE IN SYSTEM$$ RET ; ; NORMALLY USED TO PUNCH PAPER TAPE, BUT IS ; NICE FOR AN INFINITE BIT BUCKET TO CHECK FILES. ; PUNCH: IF NOT VDM ;IF NOT PROC TECH VDM, CALL CONOT ENDIF RET ;RETURN FROM PUNCH. ; ; NORMALLY USED TO READ PAPER TAPE. ; SET UP TO READ FROM CONSOLE IN STANDARD SYSTEM. ; READER: IF NOT VDM ;IF NOT PROC TECH VDM, CALL CONIN ;READ FROM CONSOLE. ENDIF 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). ; ; 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. ; HECNT: DB 0 ;HOME ERROR COUNT. RECNT: DB 0 ;READ ERROR COUNT. WECNT: DB 0 ;WRITE ERROR COUNT. SECNT: DB 0 ;SEEK ERROR COUNT. ; ; TRTAB - DISK TRACK TABLE - PRESENT POSITION OF ; HEADS FOR UP TO 4 DRIVES. ; TRTAB: DS 4 ; NODSKS: DB 2 ;NUMBER OF DISKS. ERCNT: DS 1 ;ERROR COUNT FOR RETRIES. SERCNT: DS 1 ;SEEK RETRY COUNTER. TEMP: DS 1 ;TEMPORARY STORAGE. TRK: DS 1 ;CURRENTLY SELECTED TRACK. SECT: DS 1 ;CURRENTLY SELECTED SECTOR. DMAADD: DS 2 ;CURRENT READ/WRITE ADDRESS. DISKNO: DB 0 ;CURRENT DISK NUMBER. STATIN: DB 0 ;SAVED INPUT STATUS END