;******************************************************** ;* * ;* -- CUSTOM BIOS FOR CP/M VERSION 2.2 -- * ;* * ;* Russell Smith 7-October-1980 * ;* Modified 6/16/82 by Robert Edison for TM-848 * ;******************************************************** ; ; MSIZE EQU 60 ;MEMORY CAPACITY IN KBYTES MONITR EQU 0F000H ;BASE OF SYSTEM MONITOR ; ; MONITOR ENTRY POINTS FOR MODIFIED CBIOS ; HOMER EQU 0F6E9H READR EQU 0F72AH WRITR EQU 0F71FH SEEKR EQU 0F6FBH PAUSE EQU 0F7A8H FORCE EQU 0F7AEH TURN1 EQU 0F7C9H SEL2R EQU 0F6CBH SEL1R EQU 0F6C5H ; ; OTHER STUFF FROM MONITOR ; BITDAT EQU 1CH MOTOR EQU 0FF6CH ; ; CPM REFERENCE CONSTANTS ; BIAS EQU .RES.((MSIZE-20)*1024)-200H ;remove the -200H for EA00 CCP EQU .RES.3400H+BIAS BDOS EQU .RES.CCP+806H CBIOS EQU .RES.CCP+1600H ; ; ; ORG CBIOS ; JP BOOT ;STANDARD JUMP TABLE TO BVECTR: JP WBOOT ;THE SUBROUTINES OF CBIOS SVECTR: JP CONST IVECTR: JP CONIN OVECTR: JP CONOUT JP OPNPRT ;LIST DEVICE VECTOR JP CONOUT ;PUNCH DEVICE VECTOR JP CONIN ;READER DEVICE VECTOR JP HOME JP SELECT JP SEEK JP SETSEC JP SETPTR JP READ JP WRITE JP CONST ;LIST DEVICE STATUS VECTOR JP TRANS ; ; ; BOOT: XOR A LD (0003H),A ;RESET IOBYTE TO ZEROS LD HL,SIGNON CALL PMSG ;PRINT SIGNON MESSAGE JR GOCPM ; ; WBOOT: LD SP,STACK LD C,0 CALL SELECT ;SELECT UNIT 0 CALL HOME ;SEEK TRACK ZERO LD HL,.RES.3400H+BIAS LD BC,0D02H CALL RDLOOP ;READ EVEN SECTORS ON TRK 0 LD HL,.RES.3480H+BIAS LD BC,0C03H CALL RDLOOP ;READ ODD SECTORS ON TRK 0 LD C,1 CALL SEEK ;SEEK TO TRACK 1 JR NZ,BOMB LD HL,.RES.4080H+BIAS LD BC,0A01H CALL RDLOOP ;READ ODD SECTORS ON TRK 1 LD HL,.RES.4100H+BIAS LD BC,0902H CALL RDLOOP ;READ EVEN SECTORS ON TRK 1 GOCPM: LD A,0C3H ;STORE JUMP VECTORS IN RAM LD (00H),A LD HL,CBIOS+3 ;JUMP TO CBIOS WARM BOOT AT 00H LD (01H),HL LD (05H),A LD HL,BDOS ;JUMP TO BDOS GOES AT 05H LD (06H),HL LD (38H),A LD HL,MONITR ;JUMP TO MONTR GOES AT 38H LD (39H),HL LD BC,0080H CALL SETPTR ;MAKE DISK BUFFER=0080H LD C,0 JP CCP ; ; RDLOOP: LD (POINTR),HL ;STORE ADDR. PASSED IN HL LD A,C LD (SECTOR),A ;STORE SECT# PASSED IN C PUSH HL PUSH BC CALL READ ;READ THE SPECIFIED SECTOR POP BC POP HL JR NZ,BOMB INC H ;BUMP LOAD ADDRESS BY 256 INC C INC C ;BUMP SECTOR# BY 2 DJNZ RDLOOP RET ; ; BOMB: LD HL,DEAD CALL PMSG LOOP: JP LOOP DEAD: DEFB CR DEFB LF DEFM 'cannot boot CP/M $' ; ; ; CONST: JP MONITR+6 ;MONITOR CONSOLE STATUS RTN. ; ; CONIN: JP MONITR+9 ;MONITOR CONSOLE INPUT RTN. ; ; CONOUT: LD A,C JP MONITR+12 ;MONITOR CONSOLE OUTPUT RTN. ; ; OPNPRT: LD A,0FH ;PORT B=OUTPUT OUT (0BH),A ;PIO/B CONTROL PORT LD A,1CH ;INTERRUPT VECTOR B OUT (0BH),A ;LOAD VECTOR REGISTER LD A,87H ;ENABLE INTERRUPTS ; PUSH HL LD HL,PIOINT ;GET INTERRUPT DEST ADDRESS LD (0FF1CH),HL ;STORE IT AT VECTOR LD HL,PRTCHR ;OVERWRITE JP TO OPNPRT LD (OVECTR+4),HL ;SINCE CP/M NOW GOES TO PRTCHR POP HL LD A,0FFH ;NON-ZERO TO A-REG LD (INTPND),A ;INTERRUPT NOT PENDING ; ; PRTCHR: LD A,(INTPND) ;INTERRUPT PENDING? OR A JR Z,PRTCHR ;IF YES, WAIT TILL IT'S SERVICED XOR A ;ELSE ZERO FLAG LD (INTPND),A ;SET INTERRUPT PENDING AGAIN LD A,C ;GET CHARACTER TO PRINT CPL ;INVERT DATA OUT (0AH),A ;PRINT IT RET ; INTPND DEFS 1 ;INTERRUPT PENDING FLAG ; PIOINT: PUSH AF ;INTERRUPT SERVICE ROUTINE LD A,0FFH ;SET FLAG TO NON-ZERO VALUE LD (INTPND),A POP AF EI RETI ; ;******************************************************** ;* * ;* DISK I/O SUBROUTINES FOR CP/M CBIOS * ;* * ;******************************************************** ; ; ; SECTOR TRANSLATE TABLE FOR STANDARD ; 1 IN 6 INTERLEAVE FACTOR ; SECTAB: DEFB 1 DEFB 7 DEFB 13 DEFB 19 DEFB 25 DEFB 5 DEFB 11 DEFB 17 DEFB 23 DEFB 3 DEFB 9 DEFB 15 DEFB 21 DEFB 2 DEFB 8 DEFB 14 DEFB 20 DEFB 26 DEFB 6 DEFB 12 DEFB 18 DEFB 24 DEFB 4 DEFB 10 DEFB 16 DEFB 22 ; ; ; DISK PARAMETER BLOCK FOR STANDARD 8" FLOPPY ; DPBLK: DEFW 26 ;SECTORS PER TRACK DEFB 3 ;BLOCK SHIFT CONST. DEFB 7 ;BLOCK MASK CONST. DEFB 0 ;EXTENT MASK CONST. DEFW 242 ;MAX BLOCK# DEFW 63 ;MAX DIRECTORY ENTRY# DEFB 11000000B ;ALLOCATION MASK MSB DEFB 00000000B ;' ' LSB DEFW 16 ;CHECK SIZE DEFW 2 ;RESERVED TRACKS ; ; ; DISK PARAMETER HEADERS FOR A 4 DISK SYSTEM ; DPHTAB: DEFW SECTAB DEFW 0000H ;DPH FOR UNIT 0 DEFW 0000H DEFW 0000H DEFW DIRBUF DEFW DPBLK DEFW CHK0 DEFW ALL0 DEFW SECTAB DEFW 0000H ;DPH FOR UNIT 1 DEFW 0000H DEFW 0000H DEFW DIRBUF DEFW DPBLK DEFW CHK1 DEFW ALL1 DEFW SECTAB DEFW 0000H ;DPH FOR UNIT 2 DEFW 0000H DEFW 0000H DEFW DIRBUF DEFW DPBLK DEFW CHK2 DEFW ALL2 DEFW SECTAB DEFW 0000H ;DPH FOR UNIT 3 DEFW 0000H DEFW 0000H DEFW DIRBUF DEFW DPBLK DEFW CHK3 DEFW ALL3 ; ; ; ; SETSEC: LD A,C LD (SECTOR),A ;STORE SECTOR NUMBER PASSED RET ; VIA BC ; ; TRANS: EX DE,HL ;ADD TRANSLATION TABLE ADDRESS ADD HL,BC ; PASSED IN DE TO SECTOR# IN BC LD L,(HL) LD H,0 ;LOOKUP PHYSICAL SECTOR NUMBER RET ; AND RETURN IT IN HL ; ; SETPTR: LD (POINTR),BC ;STORE DATA POINTER PASSED RET ; VIA BC ; ; ; SELECT: LD HL,0 ;PREP TO CHECK FOR MAX UNIT# LD A,C CP 4 RET NC ;RETURN WITH HL=0 IF C > 3 LD (UNIT),A ;STORE C AS NEW DRIVE UNIT# LD L,A ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ;MULTIPLY UNIT# BY 16 LD DE,DPHTAB ADD HL,DE ;ADD START ADDRESS OF DHP BLOCK PUSH HL LD C,A ;LOAD C WITH DISK DRIVE NUMBER LD B,0 ;LOAD B WITH SEEK SPEED FOR THIS DRIVE IN A,(BITDAT) ;GET CURRENT DRIVE SELECTED LD B,A AND 0F8H ;MASK OFF THE DRIVE NUMBER OR C ;INSERT THE NEW DRIVE TO BE SELECTED OUT (BITDAT),A ;SELECT THE DRIVE CALL MOTRON ;TURN ON THE DISK SPINDLE MOTOR JP Z,SELDRV ;IF DRIVE BECOMES READY CONTINUE POP HL LD A,80H ;ELSE REPORT SELECT ERROR SEL1: LD C,1 CALL REPORT JR NZ,SEL2 ;JUMP IF COMMAND ABORT INDICATED LD A,(UNIT) ;ELSE TRY TO SELECT THE DRIVE AGAIN LD C,A JR SELECT SEL2: LD HL,0 ;DISABLE FURTHER BIOS CALLS BY RET ; INDICATING SELECT ERROR TO BDOS ; ; SELDRV: CALL SEL2R ;CONTINUE SELECT ROUTINE IN MONITOR POP HL RET Z ;EXIT IF EVERYTHING IS OK JR SEL1 ;ELSE ATTEMPT TO RE-SELECT ; ; MOTRON: LD A,5 ;SET UP TIMER FOR 5 SECONDS LD (MOTOR),A CALL PAUSE ;WAIT A WHILE BEFORE ISSUING DISK COMMAND CALL FORCE ;GET DRIVE STATUS RET Z ;RETURN IF READY CALL TURN1 ;ELSE WAIT UNTIL IT DOES OR ABOUT 3 SEC. CALL FORCE ;GET STATUS AGAIN: Z=1 IF READY RET ; READY: LD A,(UNIT) ;GET CURRENT DRIVE NUMBER LD C,A IN A,(BITDAT) ;READ DRIVE SELECT MUX AND 0F8H ;MASK OFF DRIVE NUMBER OR C ;INSERT CURRENT DRIVE NUMBER OUT (BITDAT),A ;RESELECT DRIVE-ALSO STARTS SPINDLE MOTOR CALL MOTRON ;SET UP TIMER AND WAIT TILL DRIVE IS READY RET ; ; HOME: CALL READY ;MAKE SURE DRIVE IS SPINNING RET NZ ;IF NOT THEN DON'T CONTINUE CALL HOMER+4 ;CALL HOME ROUTINE IN MONITOR RET Z ;RETURN IF ALL WENT WELL LD C,2 CALL REPORT JR Z,HOME ;RE-TRY HOME IF ERROR INDICATED RET ; ; SEEK: LD A,C ;GET TRACK # FROM C LD (TRACK),A ;SAVE TRACK NUMBER CALL READY ;RESELECT DRIVE AND WAIT TILL READY RET NZ ;EXIT IF IT DOESN'T BECOME READY LD A,(TRACK) ;GET TRACK TO SEEK LD C,A CALL SEEKR+4 ;CALL SEEK ROUTINE IN MONITOR RET Z ;EXIT IF NO ERRORS INDICATED LD C,2 CALL REPORT ;REPORT SEEK ERROR TO CONSOLE RET NZ ;RETURN PERMANENT ERROR UNLESS LD A,(TRACK) ; RE-TRY REQUEST IS INDICATED LD C,A JR SEEK ; ; ; READ: CALL READY ;ATTEMPT TO GET DRIVE READY RET NZ ;RETURN IF IT WON'T LD HL,(POINTR) ;GET DISK BUFFER POINTER LD A,(SECTOR) ;GET SECTOR TO READ LD C,A CALL READR+4 ;CALL READ ROUTINE IN MONITOR RET Z ;RETURN IF NO ERRORS LD C,3 ;INDICATE READ ERROR TO HANDLER CALL REPORT ;REPORT DISK ERROR TO CONSOLE JR Z,READ ;RE-TRY READ IF INDICATED RET ; ; ; WRITE: CALL READY ;RE-SELECT DRIVE AND WAIT TILL READY RET NZ ;DON'T CONTINUE IF IT'S NOT READY LD HL,(POINTR) ;GET BUFFER POINTER LD A,(SECTOR) ; AND SECTOR TO WRITE LD C,A CALL WRITR+4 ;CALL WRITE ROUTINE IN MONITOR RET Z ;RETURN IF NO ERRORS LD C,4 ;INDICATE WRITE ERROR TO HANDLER CALL REPORT ;REPORT DISK ERROR TO CONSOLE JR Z,WRITE ;RE-TRY WRITE IF INDICATED RET ;ELSE RETURN PERMANENT ERROR ; ; REPORT: LD (FLAGS),A ;STORE 1771 I/O STATUS FLAGS LD A,C LD (CLASS),A ;STORE COMMAND CLASS OF ERROR LD HL,DSKMSG CALL PMSG ;PRINT OUT START OF MESSAGE DEC HL LD A,(CLASS) LD B,A REP1: CALL SKIP ;SKIP TO NEXT '$' IN STRING @ HL DJNZ REP1 CALL PMSG ;PRINT STRING NOW POINTED TO BY HL LD HL,ERRMSG CALL PMSG ;PRINT 'error ' AFTER TYPE LD A,(FLAGS) RLA ;TEST FIRST FOR DRIVE-NOT-READY ERROR JR C,REP8 ; AND JUMP IF THAT IS THE PROBLEM LD E,A ;GET REMAINING 1771 ERROR BITS INTO E LD HL,RWERRS LD A,(CLASS) CP 3 ;DETERMINE IF SELECT/SEEK OF R/W ERROR JR NC,REP2 LD HL,SKERRS ;POINT HL TO PROPER SET OF MESSAGES REP2: LD B,5 RES 0,D REP4: SLA E ;SHIFT OUT A 1771 STATUS REG BIT JR NC,REP5 LD C,',' BIT 0,D CALL NZ,OVECTR ;PRINT COMMA BETWEEN STRINGS IF D=1 CALL PMSG ;THEN PRINT ERROR MESSAGE @ HL SET 0,D ;FLAG THAT A STRING WAS PRINTED JR REP6 REP5: CALL SKIP ;SKIP TO NEXT STRING @ HL RES 0,D ;FLAG THAT A STRING WAS SKIPPED REP6: DJNZ REP4 ;REPEAT FOR ALL 5 POSSIBLE ERRORS ; REP7: LD A,1 OR A ;RETURN PERM ERROR INDICATION IN A RET ; REP8: LD HL,RDYMSG CALL PMSG ;PRINT DISK-NOT-READY MESSAGE CALL IVECTR ; AND WAIT FOR CONSOLE INPUT CP 'C'-64 JR Z,REP7 XOR A ;RETURN A=0 IF SOMETHING OTHER THAN RET ; CONTROL-C WAS TYPED AT THE CONSOLE ; SKIP: PUSH BC ;SAVE BC LD B,255 LD A,'$' CPIR ;SCAN MEMORY LOOKING FOR '$' POP BC RET ; ; ; ; CHARACTER STRING OUTPUT ROUTINE. PRINTS ASCII DATA ; POINTED TO BY HL UNTIL A DOLLAR SIGN IS ENCOUNTERED PMSG: LD A,(HL) ;HL POINTS TO ASCII STRING CP '$' INC HL RET Z LD C,A ;PRINT CHARACTER IF NOT DOLLAR SIGN CALL OVECTR JR PMSG ; ; LF EQU 0AH ;LINE FEED CR EQU 0DH ;CARRIAGE RETURN DSKMSG: DEFB CR DEFB LF DEFM 'bios $' DEFM 'select $' DEFM 'seek $' DEFM 'read $' DEFM 'write $' ERRMSG: DEFM 'error $' SKERRS: DEFM '$' DEFM '$' DEFM 'cannot seek$' DEFM 'bad crc$' DEFM 'cannot restore$' RDYMSG: DEFM 'drive not ready -$' RWERRS: DEFM 'write protected$' DEFM 'write fault$' DEFM 'record not found$' DEFM 'bad crc$' DEFM 'data overrun$' SIGNON: DEFB CR DEFB LF DEFM '60k CP/M vers 2.2' CRLF: DEFB CR DEFB LF DEFB '$' ; ; UNIT: DEFS 1 TRACK: DEFS 1 SECTOR: DEFS 1 POINTR: DEFS 1 FLAGS: DEFS 1 CLASS: DEFS 1 DEFS 32 STACK: DEFS 1 ;LOCAL STACK FOR WARM BOOT ; ;******************************************************** ;* * ;* DISK I/O BUFFERS FOR BDOS FILE HANDLER * ;* * ;******************************************************** ; ; ; DIRBUF: DEFS 128 ;SCRATCH DIRECTORY BUFFER ; ALL0: DEFS 32 ;UNIT 0 ALLOCATION BUFFER CHK0: DEFS 16 ;UNIT 0 CHECK VECTOR ALL1: DEFS 32 ;UNIT 1 ALLOCATION VECTOR CHK1: DEFS 16 ;UNIT 1 CHECK VECTOR ALL2: DEFS 32 ;UNIT 2 ALLOCATION VECTOR CHK2: DEFS 16 ;UNIT 2 CHECK VECTOR ALL3: DEFS 32 ;UNIT 3 ALLOCATION VECTOR CHK3: DEFS 16 ;UNIT 3 CHECK VECTOR ; ; ; ; ; END