Commodore CP/M v2.2 for the C64
Custom BIOS
; CUSTOM BIOS FOR CP/M 2.2 ON COMMODORE-64
;
; COPYRIGHT (C) 1982
; COMMODORE INTERNATIONAL
;
; THIS VERSION HAS THE FOLLOWING ATTRIBUTES:
;
; 1. MEMORY MAP SET UP FOR 52K RAM SYSTEM
; WITH I/O AND DRIVERS BY BOOT65
;
; 2. DISK TABLES AND VECTORS INCLUDED FOR
2 DRIVES
;
; 3. THE INTEL I/O BYTE IS NOT IMPLEMENTED
;
; 4. PUNCH AND READER ARE NULL ROUTINES
;
; 5. KEYBOARD AND MESSAGE TABLES ARE PART OF
; BIOS65
;
; 6. A 20K TO 48K BYTE CP/M ENVIRONMENT CAN BE
; SUPPORTED ON THE COMMODORE-64 (44K WITH IEEE)
;
; 7. VIRTUAL DRIVE B IS SUPPORTED FOR 1540
;
; 8. DRIVE B IS NOT VIRTUAL ON IEEE DISK.
;
;
BASE EQU 0000H ;BEGINNING OF ADDRESSABLE RAM
;
MSIZE EQU 44 ;CP/M VERSION MEMORY SIZE IN KILOBYTES
;
; "BIAS" IS ADDRESS OFFSET FROM 3400H FOR MEMORY SYSTEMS
; THAN 20K (REFERRED TO AS "B" THROUGHOUT THE TEXT).
;
BIAS EQU (MSIZE-20)*1024
;
; NOTE: TO CREATE MOVCPM, THE FOLLOWING CCP EQUATES
; ARE USED:
;
;CCP EQU 0000H ;FOR BIOS0.HEX
;CCP EQU 0100H ;FOR BIOS1.HEX
;
CCP EQU 3400H+BIAS ;BASE OF CCP
BDOS EQU CCP+806H ;BASE OF BDOS
BIOS EQU CCP+1600H ;BASE OF BIOS
CDISK EQU BASE+0004H ;CURRENT DISK NUMBER 0=A,...,15=P
IOBYTE EQU BASE+0003H ;INTEL I/O BYTE
TRANS EQU 0000H ;0 IMPLIES NO TRANSLATION
ENTRY EQU 0005H ;BDOS ENTRY VECTOR
;
; THE FOLLOWING EQUATES DEFINE THE COMMON MEMORY FOR
; PASSING DATA TO AND FROM THE 6510 I/O ROUTINES
;
HSTBUF EQU 0F800H ;256 BYTE DISK BUFFER
CMD EQU 0F900H ;COMMAND REGISTER
DATA EQU 0F901H ;DATA REGISTER
SECTOR EQU 0F902H ;SECTOR REGISTER
TRACK EQU 0F903H ;TRACK REGISTER
DISKNO EQU 0F904H ;DRIVE NUMBER REGISTER
KYCHAR EQU 0F905H ;KEYBOARD CHARACTER REGISTER
IOTYPE EQU 0FCFFH ;IO CONFIGURATION BYTE
;
; THE Z80 SHUTS ITSELF OFF BY WRITING "OFF" TO
; THE LOCATION "MODESW"
;
OFF EQU 1
MODESW EQU 0CE00H
;
; THE FOLLOWING ARE THE COMMANDS TO THE 6510 I/O ROUTINES
;
VICRD EQU 0 ;READ SPECIFIED SECTOR
VICWR EQU 1 ;WRITE SPECIFIED SECTOR
VICIN EQU 2 ;DO A KEYBOARD SCAN
VICOUT EQU 3 ;OUTPUT DATA TO SCREEG
VICPST EQU 4 ;GET PRINTER STATUS
VICPRT EQU 5 ;SEND CHARACTER TO PRINTER
VICFMT EQU 6 ;FORMAT DISK COMMAND
AUX1 EQU 7 ;JUMP TO $0E00 IN 6510 SPACE
AUX2 EQU 8 ;JUMP TO $0F00 IN 6510 SPACE
INDIR EQU 9 ;JUMP INDIRECT VIA 0F906
;
;
ORG BIOS ;ORIGIN OF THIS PROGRAM
NSECTS EQU ($-CCP)/256 ;WARM START SECTOR COUNT
;
; JUMP VECTOR FOR INDIVIDUAL SUBROUTINES
JMP BOOT ;COLD START
WBOOTE: JMP WBOOT ;WARM START
JMP CONST ;CONSOLE STATUS
JMP CONIN ;CONSOLE CHARACTER IN
JMP CONOUT ;CONSOLE CHARACTER OUT
JMP LIST ;LIST CHARACTER OUT
JMP PUNCH ;PUNCH CHARACTER OUT
JMP READER ;READER CHARACTER OUT
JMP HOME ;MOVE HEAD TO HOME POSITION
JMP SELDSK ;SELECT DISK
JMP SETTRK ;SET TRACK NUMBER
JMP SETSEC ;SET SECTOR NUMBER
JMP SETDMA ;SET DMA ADDRESS
JMP READ ;READ DISK
JMP WRITE ;WRITE DISK
JMP LISTST ;RETURN LIST STATUS
JMP SECTRAN ;SECTOR TRANSLATE
;
KYBDMD: DB 00H ;CAPS LOCK FLAG
;
; FIXED DATA TABLES FOR TWO DRIVES
; DISK PARAMETER HEADER FOR DISK 00
DPBASE: DW TRANS,0000H
DW 0000H,0000H
DW DIRBF,DPBLK
DW CHK00,ALL00
; DISK PARAMETER HEADER FOR DISK 01
DW TRANS,0000H
DW 0000H,0000H
DW DIRBF,DPBLK
DW CHK01,ALL01
;
;
DPBLK: ;DISK PARAMETER BLOCK, COMMON TO ALL DISKS
DW 34 ;SECTORS PER TRACK
DB 3 ;BLOCK SHIFT FACTOR
DB 7 ;BLOCK MASK
DB 0 ;NULL MASK
DW 135 ;DISK SIZE-1
DW 63 ;DIRECTORY MAX
DB 192 ;ALLOC 0
DB 0 ;ALLOC 1
DW 16 ;CHECK SIZE
DW 2 ;TRACK OFFSET
;
; END OF FIXED TABLES
;
; MEMORY INITIALIZED WHEN BIOS READ IN AT BOOT TIME
;
LASTKY: DB 40H ;VECTOR OF LAST KEY PRESSED
TOGGLE: DB 00H ;CAPS LOCK HOUSEKEEPING
CSTAT: DB 00H ;CHARACTER AVAILABLE FLAG
MSGPTR: DW 0000H ;MESSAGE POINTER
TBLPTR: DW 0FD00H ;KEYBOARD CODE TABLE
MSGTBL: DW 0FC00H ;MESSAGE VECTOR TABLE
;
; MISC. CONSOLE EQUATES
;
SHFTST EQU 0F28DH ;CONTROL,COMMODORE,SHIFT KEYS
FLASH EQU 0F0CCH ;CURSOR FLASH ENABLE
CURSOR EQU 0F0CFH ;CURSOR CHARACTER
;
; INDIVIDUAL SUBROUTINES TO PERFORM EACH FUNCTION
BOOT:
MVI A,20H ;ASCII SPACE
STA CURSOR ;SET UP CURSOR
XRA A ;ZERO IN THE ACCUM
STA IOBYTE ;CLEAR THE IOBYTE
STA CDISK ;SELECT DISK ZERO
STA CURDSK ;CLEAR VIRTUAL DISK POINTER
STA HSTACT ;HOST BUFFER INACTIVE
STA UNACNT ;CLEAR UNALLOC COUNT
MVI A,0C3H ;C3 IS JUMP OPCODE
STA 0+BASE ; FOR JUMP TO WBOOT
LXI H,WBOOTE ;WBOOT ENTRY POINT
SHLD 1+BASE ;SET ADDRESS FIELD
;
STA 5+BASE ;JUMP TO BDOS OPCODE
LXI H,BDOS ;BDOS ENTRY POINT
SHLD 6+BASE ;SET ADDRESS FIELD
;
LXI B,80H+BASE ;DEFAULT DMA ADDRESS
CALL SETDMA
;
LXI D,SIGNON ;DE POINTS TO SIGNON MSG
MVI C,9 ;PRINT STRING FUNCTION
CALL ENTRY ;GO TO BDOS
JMP GOCPM1 ;GET READY FOR CCP
;
SIGNON: DB 0CH,0AH ;CLEAR SCREEN
DB ' COMMODORE 64 20k CP/M vers 2.2'
DB 0DH,0AH,0AH
DB ' Copyright (C) 1979, Digital Research',0DH,0AH
DB ' Copyright (C) 1982, Commodore',0DH,0AH
DB 0AH,'$' ;END OF STRING MARKER
;
;
WBOOT:
LXI SP,80H+BASE ;USE SPACE BELOW BUFFER FOR STACK
MVI C,0 ;SELECT DISK 0
CALL SELDSK
XRA A ;FORCE DRIVE A
STA DISKNO ;ABSOLUTY, POSITIVELY
CALL CHGDSK ;IF NOT ALREADY SELECTED
CALL HOME ;GO TO TRACK 00
MVI A,0DH ;CARRIAGE RETURN
CALL COUT5 ;OUTPUT IT
;
LXI D,CCP ;START OF LOAD
MVI B,NSECTS
MVI H,1 ;TRACK NUMBER
MVI L,6 ;SECTOR NUMBER
LOAD1: MOV A,H
STA TRACK
MOV A,L
STA SECTOR
MVI A,VICRD ;DISK READ COMMAND
CALL IO6510
;
LDA DATA
ORA A
JNZ LOAD1 ;ERROR IF NOT ZERO
PUSH H
PUSH B
LXI B,256
LXI H,HSTBUF ;DISK BUFFER
DB 0EDH ;LDIR INSTRUCTION
DB 0B0H
MVI C,'*' ;SHOW IT'S LOADING
CALL CONOUT
POP B
POP H
DCR B ;DECREMENT SECTOR COUNT
JZ GOCPM
INR L ;NEXT SECTOR
MOV A,L
CPI 17
JC LOAD1
INR H
MVI L,0
JMP LOAD1
; END OF LOAD OPERATION, SET PARAMETERS AND GO TO CP/M
GOCPM:
MVI A,0C3H ;C3 IS A JMP INSTRUCTION
STA 0+BASE ;FOR JMP TO WBOOT
LXI H,WBOOTE ;WBOOT ENTRY POINT
SHLD 1+BASE ;SET ADDRESS FIELD FOR JMP AT 0
;
STA 5+BASE ;FOR JMP TO BDOS
LXI H,BDOS ;BDOS ENTRY POINT
SHLD 6+BASE ;ADDRESS FIELD OF JUMP AT 5 TO BDOS
;
LXI B,80H+BASE ;DEFAULT DMA ADDRESS IS 80H
CALL SETDMA
;
;
GOCPM1: LDA CDISK ;GET CURRENT DISK NUMBER
MOV C,A ;SEND TO THE CCP
JMP CCP ;GO TO CP/M FOR FURTHER PROCESSING
;
;
; MAIN ROUTINE TO TRANSFER EXECUTION TO 6510
;
IO6510: STA CMD ;PUT A IN 6510 COMMAND REGISTER
MVI A,OFF
STA MODESW ;TURN OFF Z80
NOP ;REQUIRED BY HARDWARE
RET
;
;
;
CONST: ;CONSOLE STATUS, RETURN 0FFH IF CHARACTER READY, 00H IF NOT
LHLD MSGPTR ;MESSAGE MODE?
MOV A,H
ORA L
MVI A,0FFH ;DATA READY FLAG
RNZ ;RETURN IF MSGPTR<>0
;
LDA CSTAT ;ALREADY A CHAR?
ANA A
RNZ ;YES IF NOT 0
;
MVI A,VICIN ;CHECK KEYBOARD COMMAND
CALL IO6510
;
LDA SHFTST ;GET STATUS OF CONTROL KEYS
ANI 02H ;CHECK FOR COMMODORE KEY
JZ CONST0 ;JUMP IF NOT PRESSED
;
LDA TOGGLE ;IS THIS AN UPSTROKE?
ANA A
JNZ CONST0 ;NO,WAITING TO RELEASE
;
LDA KYBDMD ;GET CAPS MODE FLAG
XRI 01H ;TOGGLE MODE BIT
STA KYBDMD
MVI A,1
CONST0: STA TOGGLE
;
LDA KYCHAR ;GET SCANNED DATA
CPI 3AH ;BAD CONTROL DATA
JZ CONST1
;
CPI 3DH ;BAD CONTROL DATA
JZ CONST1
;
LXI H,LASTKY ;COMPARE WITH PREVIOUS
CMP M ; SCAN DATA
JNZ CONST2 ;IF DIFFERENT, NEW KEY
;
CONST1: XRA A ;DATA NOT READY FLAG
STA CSTAT ;SAVE FOR LATER
RET
;
CONST2: PUSH PSW
LXI B,500
CONST3: DCX B ;DELAY FOR KEYBOUNCE
MOV A,C
ORA B
JNZ CONST3
;
MVI A,VICIN ;GET CHARACTER AGAIN
CALL IO6510
;
POP PSW
LXI H,KYCHAR
CMP M
JNZ CONST1 ;IF NOT 0, KEY NOT DONE BOUNCING
;
STA LASTKY ;UPDATE LAST KEY
CPI 40H ;IF 40H, NO KEY PRESSED
JZ CONST1
;
MVI A,0FFH ;DATA READY FLAG
STA CSTAT ;SAVE FOR LATER
RET
;
CONIN: ;CONSOLE CHARACTER INTO REGISTER A
MVI A,0 ;TURN ON CURSOR
STA FLASH
;
LHLD MSGPTR ;ARE WE IN MESSAGE MODE?
MOV A,H
ORA L
JNZ CONIN5
;
;
CONIN1: CALL CONST ;CHECK CONSOLE STATUS
ORA A
JZ CONIN1 ;UNTIL NEW CHARACTER
;
XRA A
STA CSTAT ;CLEAR CSTAT
CONIN2: LDA KYBDMD ;UNSHIFT=0, CAPS=1
MOV B,A
LDA SHFTST ;GET MODIFIER STATUS
ANI 01H ;IS A SHIFT KEY DOWN?
JZ CONIN3 ;JUMP IF NO
;
MVI B,2 ;SHIFT=2
CONIN3: LDA SHFTST ;GET MODIFIER STATUS
ANI 04H ;IS THE CONTROL KEY DOWN?
JZ CONIN4 ;JUMP IF NO
;
MVI B,3 ;CONTROL=3
CONIN4: LDA LASTKY ;GET KEY POSITION
ADD A ;*2
ADD A ;*4
ADD B ;ADD IN OFFSET
LHLD TBLPTR ;GET BEGINNING OF KEYTBL
ADD L ;VECTOR INTO TABLE
MOV L,A
MVI A,0
ADC H
MOV H,A
MOV A,M ;GET CHARACTER FROM TABLE
CPI 80H ;MESSAGE IF <7FH
JC CONIN7 ;JUMP IF ASCII CHARACTER
;
LHLD MSGTBL ;GET BEGINNING OF MVTBL
ANI 7FH ;STRIP OF MESSAGE BIT
ADD A ;*2
ADD L ;VECTOR INTO TABLE
MOV L,A
MVI A,0
ADC H
MOV H,A
MOV A,M ;LOW ORDER BYTE
INX H
MOV H,M ;HIGH ORDER BYTE
MOV L,A
CONIN5: MOV B,M ;GET CHARACTER
INX H ;CHECK NEXT CHARACTER
MOV A,M
ANA A
JNZ CONIN6 ;IF 0, B HAS LAST CHAR
;
LXI H,0000H ;END OF MESSAGE MODE
CONIN6: SHLD MSGPTR ;SAVE MESSAGE POINTER
MOV A,B ;CHECK CHARACTER
ANA A ;MAYBE 1ST IS 0
JZ CONIN1 ;IF 0, NOT A CHARACTER
;
CONIN7: PUSH PSW ;SAVE CHARACTER
MVI A,1
STA FLASH ;TURN OFF CURSOR
MVI C,' ' ;GET SPACE
CALL CONOUT ;PUT IT BACK
MVI A,9DH ;CURSOR LEFT COMMAND
CALL COUT5 ;BYPASS FILTER
POP PSW ;GET CHARACTER
RET ;DONE
;
CONOUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C
LDA IOTYPE ;GET CONFIGURATION BYTE
ANI 10H ;BIT 4 = 1 TO IGNORE FILTER
MOV A,C ;GET TO ACCUMULATOR
JNZ COUT5 ;PRINT AS RECEIVED
;
CALL SWAP ;EXCHANGE UPPER AND LOWER CASE
CPI 0CH ;ASCII CLEAR SCREEN?
JNZ COUT1 ;JUMP IF NO
;
MVI A,93H ;COMMODORE CLEAR SCREEN CMD
JMP COUT5
;
COUT1: CPI 08H ;ASCII BACKSPACE?
JNZ COUT2 ;JUMP IF NO
;
MVI A,14H ;COMMODORE BACKSPACE CMD
JMP COUT5
;
COUT2: CPI 0AH ;LINE FEED?
JNZ COUT3
;
MVI A,17 ;COMMODORE LINE FEED
JMP COUT5
;
COUT3: CPI 0DH ;CARRIAGE RETURN?
JNZ COUT4
;
CALL COUT5
MVI A,145 ;UP 1 LINE TO NEGATE AUTO LF
JMP COUT5
;
COUT4: CPI 20H
RC ;RETURN IF UNDECODED CONTROL CHAR
CPI 80H
RNC ;RETURN IF NOT ASCII CHARACTER
;
COUT5: STA DATA ;PUT DATA IN CHARACTER REGISTER
MVI A,VICOUT ;SCREEN OUTPUT COMMAND
JMP IO6510
;
LIST: ;LIST CHARACTER FROM REGISTER C
LDA IOTYPE ;WHAT KIND OF PRINTER?
ANI 04H ;0 IF 1515, 1 IF 4022
MOV A,C ;CHARACTER TO REGISTER A
JNZ LIST2 ;JUMP IF NO SWAP
;
LDA IOTYPE
ANI 08H ;WHICH TYPE OF SWAP?
MOV A,C ;GET CHARACTER
JNZ LIST1
;
CALL SWAP ;SWAP UPPER AND LOWER CASE
JMP LIST2
;
LIST1: CALL SWAP2 ;4022 SWAP ROUTINE
LIST2: STA DATA ;PUT DATA IN REGISTER
MVI A,VICPRT ;ASSUME 1540
LIST3: JMP IO6510
;
LISTST: ;RETURN LIST STATUS (0 IF NOT READY, 1 IF READY)
MVI A,VICPST ;PRINTER STATUS COMMAND
CALL IO6510
LDA DATA ;DATA IS STATUS
RET
;
SWAP: ;SWAP UPPER AND LOWER CASE FOR COMMODORE-64
CPI 41H ;LESS THAN UC 'A'?
RC ;RETURN IF SO
;
CPI 5BH ;UC LETTER?
JC SWAP1 ;JUMP IF SO
;
CPI 61H ;LESS THAT LC 'A'
RC ;RETURN IF SO
;
CPI 7BH ;LC LETTER?
RNC ;RETURN IF NO
;
ANI 5FH ;TURN OFF BIT 5
RET
;
SWAP1: ORI 20H ;TURN ON BIT 5
RET
;
SWAP2: CPI 41H ;CY IF LESS THAN UC 'A'
RC
CPI 60H ;CY IF 40H < A < 60H
JNC SWAP3
;
ORI 80H
RET
;
SWAP3: ANI 5FH
RET
;
PUNCH: ;PUNCH CHARACTER FROM REGISTER C
MOV A,C ;CHARACTER TO REGISTER A
NOP
RET ;NULL SUBROUTINE
;
;
READER: ;READ CHARACTER INTO REGISTER A FROM READER DEVICE
MVI A,1AH ;ENTER END OF FILE FOR NOW (REPLACE LATER)
ANI 7FH ;REMEMBER TO STRIP PARITY BIT
RET
;
;
;*****************************************************
;* *
;* CP/M TO HOST DISK CONSTANTS *
;* *
;*****************************************************
BLKSIZ EQU 1024 ;CP/M ALLOCATION SIZE
HSTSIZ EQU 256 ;HOST DISK SECTOR SIZE
HSTSPT EQU 17 ;HOST DISK SECTORS/TRK
HSTBLK EQU HSTSIZ/128 ;CP/M SECTS/HOST BUFF
CPMSPT EQU HSTBLK * HSTSPT ;CP/M SECTORS/TRACK
SECMSK EQU HSTBLK-1 ;SECTOR MASK
SECSHF EQU 1 ;LOG2(HSTBLK)
;
;*****************************************************
;* *
;* BDOS CONSTANTS ON ENTRY TO WRITE *
;* *
;*****************************************************
WRALL EQU 0 ;WRITE TO ALLOCATED
WRDIR EQU 1 ;WRITE TO DIRECTORY
WRUAL EQU 2 ;WRITE TO UNALLOCATED
;
; HOME THE SELECTED DISK
HOME:
LDA HSTWRT ;CHECK FOR PENDING WRITE
ORA A
JNZ HOMED
STA HSTACT ;CLEAR HOST ACTIVE FLAG
HOMED:
RET
;
SELDSK:
;SELECT DISK
LXI H,0000H ;ERROR RETURN CODE
MOV A,C ;SELECTED DISK NUMBER
STA SEKDSK ;SEEK DISK NUMBER
CPI 2 ;MUST BE 0-1
RNC ;NO CARRY IF 2,3,...
MOV L,A ;DISK NUMBER TO HL
DAD H ;MULTIPLY BY 16
DAD H
DAD H
DAD H
LXI D,DPBASE ;BASE OF PARM BLOCK
DAD D ;HL=.DPB(CURDSK)
RET
;
SETTRK:
;SET TRACK GIVEN BY REGISTERS BC
MOV H,B
MOV L,C
SHLD SEKTRK ;TRACK TO SEEK
RET
;
SETSEC:
;SET SECTOR GIVEN BY REGISTER C
MOV A,C
STA SEKSEC ;SECTOR TO SEEK
RET
;
SETDMA:
;SET DMA ADDRESS GIVEN BY BC
MOV H,B
MOV L,C
SHLD DMAADR
RET
;
SECTRAN:
;TRANSLATE SECTOR NUMBER BC
MOV H,B
MOV L,C
RET
;
;*****************************************************
;* *
;* THE READ ENTRY POINT TAKES THE PLACE OF *
;* THE PREVIOUS BIOS DEFINTION FOR READ. *
;* *
;*****************************************************
READ:
;READ THE SELECTED CP/M SECTOR
XRA A
STA UNACNT
MVI A,1
STA READOP ;READ OPERATION
STA RSFLAG ;MUST READ DATA
MVI A,WRUAL
STA WRTYPE ;TREAT AS UNALLOC
JMP RWOPER ;TO PERFORM THE READ
;
;*****************************************************
;* *
;* THE WRITE ENTRY POINT TAKES THE PLACE OF *
;* THE PREVIOUS BIOS DEFINTION FOR WRITE. *
;* *
;*****************************************************
WRITE:
;WRITE THE SELECTED CP/M SECTOR
XRA A ;0 TO ACCUMULATOR
STA READOP ;NOT A READ OPERATION
MOV A,C ;WRITE TYPE IN C
STA WRTYPE
CPI WRUAL ;WRITE UNALLOCATED?
JNZ CHKUNA ;CHECK FOR UNALLOC
;
; WRITE TO UNALLOCATED, SET PARAMETERS
MVI A,BLKSIZ/128 ;NEXT UNALLOC RECS
STA UNACNT
LDA SEKDSK ;DISK TO SEEK
STA UNADSK ;UNADSK = SEKDSK
LHLD SEKTRK
SHLD UNATRK ;UNATRK = SECTRK
LDA SEKSEC
STA UNASEC ;UNASEC = SEKSEC
;
CHKUNA:
;CHECK FOR WRITE TO UNALLOCATED SECTOR
LDA UNACNT ;ANY UNALLOC REMAIN?
ORA A
JZ ALLOC ;SKIP IF NOT
;
; MORE UNALLOCATED RECORDS REMAIN
DCR A ;UNACNT = UNACNT-1
STA UNACNT
LDA SEKDSK ;SAME DISK?
LXI H,UNADSK
CMP M ;SEKDSK = UNADSK?
JNZ ALLOC ;SKIP IF NOT
;
; DISKS ARE THE SAME
LXI H,UNATRK
CALL TRKCMP ;SEKTRK = UNATRK?
JNZ ALLOC ;SKIP IF NOT
;
; TRACKS ARE THE SAME
LDA SEKSEC ;SAME SECTOR?
LXI H,UNASEC
CMP M ;SEKSEC = UNASEC?
JNZ ALLOC ;SKIP IF NOT
;
; MATCH, MOVE TO NEXT SECTOR FOR FUTURE REF
INR M ;UNASEC = UNASEC+1
MOV A,M ;END OF TRACK?
CPI CPMSPT ;COUNT CP/M SECTORS
JC NOOVF ;SKIP IF NO OVERFLOW
;
; OVERFLOW TO NEXT TRACK
MVI M,0 ;UNASEC = 0
LHLD UNATRK
INX H
SHLD UNATRK ;UNATRK = UNATRK+1
;
NOOVF:
;MATCH FOUND, MARK AS UNNECESSARY READ
XRA A ;0 TO ACCUMULATOR
STA RSFLAG ;RSFLAG = 0
JMP RWOPER ;TO PERFORM THE WRITE
;
ALLOC:
;NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ
XRA A ;0 TO ACCUM
STA UNACNT ;UNACNT = 0
INR A ;1 TO ACCUM
STA RSFLAG ;RSFLAG = 1
;
;*****************************************************
;* *
;* COMMON CODE FOR READ AND DRITE FOLLOWS *
;* *
;*****************************************************
RWOPER:
;ENTER HERE TO PERFORM THE READ/WRITE
XRA A ;ZERO TO ACCUM
STA ERFLAG ;NO ERRORS (YET)
LDA SEKSEC ;COMPUTE HOST SECTOR
ORA A ;CARRY = 0
RAR ;SHIFT RIGHT
STA SEKHST ;HOST SECTOR TO SEEK
;
; ACTIVE HOST SECTOR?
LXI H,HSTACT ;HOST ACTIVE FLAG
MOV A,M
MVI M,1 ;ALWAYS BECOMES 1
ORA A ;WAS IT ALREADY?
JZ FILHST ;FILL HOST IF NOT
;
; HOST BUFFER ACTIVE, SAME AS SEEK BUFFER?
LDA SEKDSK
LXI H,HSTDSK ;SAME DISK?
CMP M ;SEKDSK = HSTDSK?
JNZ NOMTCH
;
; SAME DISK, SAME TRACK?
LXI H,HSTTRK
CALL TRKCMP ;SEKTRK = HSTTRK?
JNZ NOMTCH
;
; SAME DISK, SAME TRACK, SAME BUFFER?
LDA SEKHST
LXI H,HSTSEC ;SEKHST = HSTSEC?
CMP M
JZ MATCH ;SKIP IF MATCH
;
NOMTCH:
;PROPER DISK, BUT NOT CORRECT SECTOR
LDA HSTWRT ;HOST WRITTEN?
ORA A
CNZ WRHST ;CLEAR HOST BUFF
;
FILHST:
;MAY HAVE TO FILL THE HOST BUFFER
LDA SEKDSK
STA HSTDSK
LHLD SEKTRK
SHLD HSTTRK
LDA SEKHST
STA HSTSEC
LDA RSFLAG ;NEED TO READ?
ORA A
CNZ RDHST ;YES, IN 1
XRA A ;0 TO ACCUM
STA HSTWRT ;NO PENDING WRITE
;
MATCH:
;COPY DATA TO OR FROM BUFFER
LDA SEKSEC ;MASK BUFFER NUMBER
ANI SECMSK ;LEAST SIGNIF BITS
MOV L,A ;READY TO SHIFT
MVI H,0 ;DOUBLE COUNT
DAD H ;SHIFT LEFT 7
DAD H
DAD H
DAD H
DAD H
DAD H
DAD H
; HL HAS RELATIVE HOST BUFFER ADDRESS
LXI D,HSTBUF
DAD D ;HL = HOST ADDRESS
XCHG ;NOW IN DE
LHLD DMAADR ;GET/PUT CP/M DATA
MVI C,128 ;LENGTH OF MOVE
LDA READOP ;WHICH WAY?
ORA A
JNZ RWMOVE ;SKIP IF READ
;
; WRITE OPERATION, MARK AND SWITCH DIRECTION
MVI A,1
STA HSTWRT ;HSTWRT = 1
XCHG ;SOURCE/DEST SWAP
;
RWMOVE:
;C INITIALLY 128, DE IS SOURCE, HL IS DEST
LDAX D ;SOURCE CHARACTER
INX D
MOV M,A ;TO DEST
INX H
DCR C ;LOOP 128 TIMES
JNZ RWMOVE
;
; DATA HAS BEEN MOVED TO/FROM HOST BUFFER
LDA WRTYPE ;WRITE TYPE
CPI WRDIR ;TO DIRECTORY?
LDA ERFLAG ;IN CASE OF ERRORS
RNZ ;NO FURTHER PROCESSING
;
; CLEAR HOST BUFFER FOR DIRECTORY WRITE
ORA A ;ERRORS?
RNZ ;SKIP IF SO
XRA A ;0 TO ACCUM
STA HSTWRT ;BUFFER WRITTEN
CALL WRHST
LDA ERFLAG
RET
;
;*****************************************************
;* *
;* UTILITY SUBROUTINE FOR 16-BIT COMPARE *
;* *
;*****************************************************
TRKCMP:
;HL = .UNATRK OR .HSTTRK, COMPARE WITH SEKTRK
XCHG
LXI H,SEKTRK
LDAX D ;LOW BYTE COMPARE
CMP M ;SAME?
RNZ ;RETURN IF NOT
; LOW BYTES EQUAL, TEST HIGH 1S
INX D
INX H
LDAX D
CMP M ;SETS FLAGS
RET
;
;*****************************************************
;* *
;* WRHST PERFORMS THE PHYSICAL WRITE TO *
;* THE HOST DISK, RDHST READS THE PHYSICAL *
;* DISK. *
;* *
;*****************************************************
WRHST:
;HSTDSK = HOST DISK #, HSTTRK = HOST TRACK #,
;HSTSEC = HOST SECT #. WRITE "HSTSIZ" BYTES
;FROM HSTBUF AND RETURN ERROR FLAG IN ERFLAG.
;RETURN ERFLAG NON-ZERO IF ERROR
MVI A,VICWR ;LOAD DISK WRITE COMMAND
WRHST0: STA RW ;PUT COMMAND IN REGISTER
LDA HSTDSK ;GET HOST DISK NUMBER
STA DISKNO ; AND PUT IN COMMON AREA
CALL CHGDSK ;CORRECT VIRTUAL DISK?
WRHST2: LDA HSTTRK ;GET HOST TRACK NUMBER
INR A ;ADD 1 FOR VIC OFFSET
CPI 18 ;WE WANT TO SKIP TRACK 18
JC WRHST3 ;CARRY IF TRACK<18
INR A
WRHST3: STA TRACK ;PUT IN COMMON AREA
LDA HSTSEC ;GET HOST SECTOR NUMBER
STA SECTOR ;PUT IN COMMON AREA
LDA RW ;GET DISK COMMAND
CALL IO6510
LDA DATA ;GET DISK STATUS
STA ERFLAG ; AND STORE IN ERFLAG
RET
;
CHGDSK: MOV H,A ;SAVE DISK NUMBER
LDA IOTYPE ;BIT 0=0 FOR VIRTUAL
ANI 01
RNZ ;NOT ZERO IF 2 DRIVES
STA DISKNO ;FORCE DRIVE A
MOV A,H ;RESTORE DISK NUMBER
;
LXI H,CURDSK ;IS THIS OUR CURRENT DISK?
CMP M
RZ ;RETURN IF OK
;
MOV M,A ;SET UP NEW DISK
ADI 'A' ;FORM ASCII DRIVE LETTER
STA DSKMNT ;PUT IN MESSAGE
;
LXI H,MNTMSG ;INSERT DISK MESSAGE
CALL PMSG ;GO PRINT IT
CHGD1: CALL CONIN ;WAIT FOR RETURN
CPI 0DH
JNZ CHGD1
RET
;
RDHST:
;HSTDSK = HOST DISK #, HSTTRK = HOST TRACK #,
;HSTSEC = HOST SECT #. READ "HSHSIZ" BYTES
;INTO HSTBUF AND RETURN ERROR FLAG IN ERFLAG.
MVI A,VICRD ;DISK READ COMMAND
JMP WRHST0 ;REST IS SAME AS DISK WRITE
;
MNTMSG: DB 0DH,0AH,'Insert disk '
DSKMNT: DB 'A'
DB ' into drive 0, press return'
DB 00H
;
PMSG: MOV A,M
ANA A
RZ
PUSH H
MOV C,A
CALL CONOUT
POP H
INX H
JMP PMSG
;
;*****************************************************
;* *
;* UNITIALIZED RAM DATA AREAS *
;* *
;*****************************************************
;
SEKDSK: DS 1 ;SEEK DISK NUMBER
SEKTRK: DS 2 ;SEEK TRACK NUMBER
SEKSEC: DS 1 ;SEEK SECTOR NUMBER
;
HSTDSK: DS 1 ;HOST DISK NUMBER
HSTTRK: DS 2 ;HOST TRACK NUMBER
HSTSEC: DS 1 ;HOST SECTOR NUMBER
;
SEKHST: DS 1 ;SEEK SHR SECSHF
HSTACT: DS 1 ;HOST ACTIVE FLAG
HSTWRT: DS 1 ;HOST WRITTEN FLAG
;
UNACNT: DS 1 ;UNALLOC REC CNT
UNADSK: DS 1 ;LAST UNALLOC DISK
UNATRK: DS 2 ;LAST UNALLOC TRACK
UNASEC: DS 1 ;LAST UNALLOC SECTOR
;
ERFLAG: DS 1 ;ERROR REPORTING
RSFLAG: DS 1 ;READ SECTOR FLAG
READOP: DS 1 ;1 IF READ OPERATION
WRTYPE: DS 1 ;WRITE OPERATION TYPE
DMAADR: DS 2 ;LAST DMA ADDRESS
RW: DS 1 ;TEMPORARY COMMAND REGISTER
CURDSK: DS 1 ;VIRTUAL DISK POINTER
;
; SCRATCH RAM AREA FOR BDOS USE
BEGDAT EQU $ ;BEGINNING OF DATA AREA
DIRBF: DS 128 ;SCRATCH DIRECTORY AREA
ALL00: DS 31 ;ALLOCATION VECTOR 0
ALL01: DS 31 ;ALLOCATION VECTOR 1
CHK00: DS 16 ;CHECK VECTOR 0
CHK01: DS 16 ;CHECK VECTOR 1
;
ENDDAT EQU $ ;END OF DATA AREA
DATSIZ EQU $-BEGDAT;SIZE OF DATA AREA
END