Here is a single assembler source file, for tpasm, which generates HDBIOS.SYS file and gives a binary identical result to the file distributed by JGH. This may make it easier to follow what is happening:
Code:
;;; Acorn Z80 CP/M BIOS .processor z80 .include "defs.asm" .org $ea00alvC_Z80Tube EQU $F700alvC_CoPro EQU $FCDEDiskAcc EQU $FFA4LoadCCP EQU $FFA7alvB EQU $F5C6GBPBchn EQU alvB+$19GBPBadr EQU GBPBchn+1GBPBnum EQU GBPBchn+5GBPBptr EQU GBPBchn+9CSVd0 EQU $F4E0 ; CSV for Drive 0CSVd1 EQU CSVd0+$20 ; CSV for Drive 1ALVd0 EQU $F520 ; ALV for Drive 0ALVd1 EQU $F5C6 ; ALV for drive 1BIOS_BOOT: JP ColdBootBIOS_WBOOT: JP WarmBootBIOS_CONST: JP ConsoleStatus ; CONBIOS_CONIN: JP ConsoleIn ; CONBIOS_CONOUT: JP ConsoleOut ; CONBIOS_LIST: JP ListOut ; LST (printer)BIOS_PUNCH: JP PunchOut ; PUNBIOS_READER: JP ReaderIn ; RDRBIOS_HOME: JP DiskHomeBIOS_SELDSK: JP SelectDiskBIOS_SETTRK: JP SetTrackBIOS_SETSEC: JP SetSectorBIOS_SETDMA: JP SetDMABIOS_READ: JP DiskReadBIOS_WRITE: JP DiskWriteBIOS_LISTST: JP ListStatus ; LST (printer)BIOS_SECTRAN: JP SectorTranslateDriveCfs: DB 8DriveC: DB "$.CPMDISK",13,"*******" DPH_Base: DW $0000 ; DPH for drive 0 DW $0000 DW $0000 DW $0000 DW LF1F2 DW DPB_Acorn400k DW CSVd0 DW ALVd0 DW $0000 ; DPH for drive 1 DW $0000 DW $0000 DW $0000 DW LF1F2 DW DPB_Acorn400k DW CSVd1 DW ALVd1DPH_C: DW $0000 ; DPH for drive C DW $0000 DW $0000 DW $0000 DW LF1F2 DW DPB_HardDrive DW $0000 DW $0000 ; Set on ColdBoot; DPB for AcornCPM 400k diskDPB_Acorn400k: DW $0014 ; SPT=20 Sectors Per Track DB $04 ; BSH=4 Block Shift DB $0F ; BLM=15 Block Mask DB $01 ; EXM=1 Extent Mask DW $00C3 ; DSM=195 Disk Sector Max DW $007F ; DRM=127 Maximum directory entry number DB $C0 ; AL0=$C0 Directory occupies DB $00 ; AL1=$00 first two blocks DW $0020 ; CKS=32 Size of directory checksum vector DW $0003 ; OFF=3 Reserved tracks before logical start of disk; Total disk size is 128*(BLM+1)*(DSM+1)=392K; Physical disk size is 128*SPT*OFF+128*(BLM+1)*(DSM+1)=399.5K; DPB for 8M Hard DriveDPB_HardDrive: DW $0100 ; SPT=256 Sectors Per Track DB $05 ; BSH=5 Block Shift DB $1F ; BLM=31 Block Mask DB $01 ; EXM=1 Extent Mask DW $07FF ; DSM=2047 Disk Sector Max DW $03FF ; DRM=1023 Maximum directory entry number DB $FF ; AL0=$FF Directory occupies DB $00 ; AL1=$00 first eight blocks DW $0000 ; CKS=0 Size of directory checksum vector DW $0000 ; OFF=0 Reserved tracks before logical start of disk; Total disk size is 128*(BLM+1)*(DSM+1)=8192K; Physical disk size is 128*SPT*OFF+128*(BLM+1)*(DSM+1)=8192K .org $EA93LEA93: LD A,$83 ; Reset input stream, specify CON=UC1 RDR=TTY PUN=TTY LST=LPTLEA95: LD ($0003),A ; Reset IOBYTE LD L,$02 LD A,$02 CALL OSBYTE ; Input stream=kbd, serial enabled JP LEBBDConsoleStatus: CALL LEB02 DB $81 ; IOBYTE b0-1, test TTY/CRT/BAT/UC1 DW TTYStatus DW CRTStatus DW ReaderStatus DW UC1StatusConsoleIn: CALL LEB02 DB $01 ; IOBYTE b0-b1, input from TTY/CRT/BAT/UC1 DW TTYIn DW CRTIn DW ReaderIn DW OSRDCHConsoleOut: CALL LEB02 DB $01 ; IOBYTE b0-b1, output to TTY/CRT/BAT/UC1 DW TTYOut DW CRTOut DW ListOut DW UC1OutListOut: CALL LEB02 DB $03 ; IOBYTE b6-7, output to TTY/CRT/LPT/UL1 DW TTYOut DW CRTOut DW LPTOut DW UL1OutListStatus: CALL LEB02 DB $03 ; IOBYTE b6-7, test TTY/CRT/LPT/UL1 DW SerStatus DW TTYempty DW UL1Status DW UL1StatusPunchOut: CALL LEB02 DB $05 ; IOBYTE b4-5, output to TTY/PTP/UP1/UP2 DW TTYOut DW CRTOut DW NullOut DW NullOutReaderIn: CALL LEB02 DB $07 ; IOBYTE b2-3, input from TTY/PTR/UR1/UR2 DW TTYIn DW CRTIn DW NullIn DW NullInReaderStatus: CALL LEB02 DB $07 ; IOBYTE b2-3, test TTY/PTR/UR1/UR2 DW TTYStatus DW CRTStatus DW TTYnonempt DW TTYnonempt; Indirect character I/O call; ===========================; CALL is followed by an IOBYTE mask flag, then four addresses; If bit 7 of the mask flag is clear, then a flag is cleared; The IOBYTE is rotated by the mask flag to get an index into; the inline addresses. The selected address is then jumped to.;LEB02: POP HL LD a,(hl) INC HL ; Get IOBYTE mask flag BIT 7,A LD B,A RES 7,B JR NZ,LEB10 ; If $80+n, don't clear something XOR A LD (LF167),A ; $00+n, clear this flagLEB10: LD A,($0003) ; Get IOBYTELEB13: RLCA DJNZ LEB13 ; Rotate left by mask flag AND $06 LD D,$00 ; Index into four inline addresses LD E,A ADD HL,DE LD E,(HL) ; Jump to the inline address specified by INC HL ; IOBYTE rotated by the flag byte LD D,(HL) EX DE,HL JP (HL); Null input - return EOF; -----------------------NullIn: LD A,$1A; Null output - sink; ------------------NullOut: RET; Read UC1 status; ===============UC1Status: LD HL,LF167 ; Test flag XOR A OR (HL) ; If zero, check current input stream JR Z,LEB2E DEC (HL) ; Decrement flag XOR A RET ; Return $00LEB2E: LD HL,$FF00 LD A,$B1 CALL OSBYTE ; Read input streamLEB36: CALL LEB8C RET NZ LD A,$D8 ; Read soft key length LD HL,$ff00 CALL OSBYTE LD A,L AND A JR NZ,TTYnonempt LD HL,LF167 ; Set flag to 12 LD (HL),$0C RET; Output to UC1 - RDCH/WRCH; =========================UC1Out: LD A,C ; Send via PROUT to WRCH or TERMOUT JP $FF9E; Read CRT status; ===============CRTStatus: LD l,$00 JR LEB36 ; Jump to test buffer 0 (keyboard); CRT input (ie, KBD/VDU); =======================CRTIn: LD L,$02 ; Keyboard Input, Serial EnabledLEB56: LD A,$02 ; Set input stream CALL OSBYTE CALL OSRDCH PUSH AF ; Wait for input character LD A,L AND A JR NZ,LEB65 ; If previous stream<>0, reselect it LD L,$02 ; Otherwise, enable serial, keyboard inputLEB65: LD A,$02 ; Set input stream CALL OSBYTE POP AF RET ; Return with character read; CRT Output - VDU/KBD; ====================CRTOut: LD HL,$00F4 ; Output=*,noprint,*,nospool,printer,novduprinter,vdu,noserialLEB6F: LD A,$03 ; Enter here with HL=*FX3 value CALL OSBYTE ; Select output stream PUSH HL ; Save previous output stream XOR A ; Switch off terminal mode CALL $FFC8 PUSH AF ; Remember previous state LD A,C ; Output the character CALL OSWRCH POP AF ; Restore previous terminal state CALL $FFC8 POP HL ; Get previous output stream LD H,$00 LD A,$03 JP OSBYTE ; Restore previous output and return; Read TTY status; ===============TTYStatus: LD L,$01 ; Serial Input bufferLEB8C: LD A,$98 CALL OSBYTE ; Examine buffer JR NC,TTYnonempt ; Buffer not emptyTTYempty: XOR A RET ; Return A=$00 if emptyTTYnonempt: XOR A DEC A RET ; Return A=$FF if not empty; Console Input; =============TTYIn: LD L,$01 JR LEB56 ; Jump to read from Serial Input; TTY Output - serial output; ==========================TTYOut: LD HL,$00F7 ; Output=*,noprint,*,nospool,printer,novduprinter,novdu,serial JR LEB6FSerStatus: LD HL,$FFFD JR LEBC7 ; Jump to check SerialOut buffer with HL=-3; LPT Output - test printer before outputing; ==========================================LPTOut: CALL LEBD2 ; Test printer status; UL1 Output - output straight to printer; =======================================UL1Out: LD A,C ; Test if C=0 AND A LD HL,$001A ; Output=*,printer,*,nospool,printer,novduprinter,novdu,noserial JR NZ,LEB6F ; If C=0, jump to output to printer LD A,$06 LD L,$FF ; Set printer ignore char to $FF so can output $00 CALL OSBYTE LD HL,$001A CALL LEB6F ; Write character to printerLEBBD: LD A,$06 LD L,$00 ; Set printer ignore char to $00 JP OSBYTEUL1Status: LD HL,$FFFC ; HL=-4 - printer bufferLEBC7: LD A,$80 ; Check buffer status CALL OSBYTE LD A,L ; Return $00 if no space left (bug, should be AND H) AND A RET Z XOR A DEC A RET ; Return $FF if space available (bug, don't need XOR A); Test printer status; ===================LEBD2: LD A,($0003) ; Get IOBYTE LST and CON fields AND $C3 CP $82 RET Z ; If LST=LPT, CON=BAT, exit CALL LEC80 ; Check and return if printer available RET NZ LD A,$86 ; Read POS and VPOS CALL OSBYTE LD (LF168),HL LD A,$87 ; Read MODE. CALL OSBYTE LD A,H LD HL,$1F36 ; H=32-1 lines, L=54 AND A JR Z,LEBFD ; MODE 0, 32 lines CP $03 JR Z,LEBFB ; MODE 3, 25 lines LD HL,$0000 JR LEBFD ; Others, use 00x00LEBFB: LD H,$18 ; H=25-1 linesLEBFD: PUSH HL CALL LEC6C ; PRINT TAB(54,Y); or (0,0); CALL LEC8E DB "Printer off line" DB 0 CALL LEC80 POP HL ; Get coord back PUSH HL CALL LEC6C ; PRINT TAB(54,Y); or (0,0); CALL LEC8E DB "SPACE starts Printer Sink", 0LEC3A: CALL UL1Status JR NZ,LEC5A CALL ConsoleStatus AND A JR Z,LEC3A CALL ConsoleIn CP ' ' JR NZ,LEC3A ; Not SPACE, loop back LD L,$03 LD A,$15 CALL OSBYTE ; Flush printer buffer LD L,$00 LD A,$05 CALL OSBYTE ; Select printer sinkLEC5A: POP HL CALL LEC6C ; Get coords and PRINT TAB(54,Y); or (0,0); LD B,$19 ; Print 25 spaces to overwrite messageLEC60: PUSH BC LD C,' ' CALL ConsoleOut ; Print a space POP BC DJNZ LEC60 LD HL,(LF168) ; Get original POS/VPOS backLEC6C: PUSH BC PUSH HL ; Save HL and BC LD C,$1F CALL ConsoleOut ; VDU 31 - TAB POP HL ; Get X coord PUSH HL LD C,L CALL ConsoleOut ; Send X coord POP HL ; Get Y coord LD C,H CALL ConsoleOut ; Send Y coord POP BC ; Restore BC RET; Test if output to printer online; ================================LEC80: LD DE,$AFC8 ; Test 45,000 timesLEC83: CALL UL1Status ; Test printer buffer RET NZ ; return if not full DEC DE ; Decrement timer LD A,E OR D JR NZ,LEC83 ; Loop until timed out and buffer not emptied XOR A RET ; Return with Z set, printer offline; Print inline message until $00 byte; ===================================LEC8E: EX (SP),HL ; Get address from stack PUSH DE ; Save everything else PUSH BC PUSH AFLEC92: LD A,(HL) ; Get a byte INC HL ; step to next AND A JR Z,LEC9E ; Zero byte, end of message PUSH HL CALL LECA3 ; Print the character POP HL JR LEC92 ; Loop back for nextLEC9E: POP AF ; Restore everything POP BC POP DE EX (SP),HL ; Stack pointer RET ; and return to it; CON_ASCII; =========LECA3: CP $0D JR NZ,LECAC ; Jump to output non-<CR> CALL LECAC ; Print <CR> LD A,$0A ; and add a <LF>LECAC: LD C,A JP ConsoleOut ; Print character; Complain about disk being booted fromLECB0: CALL LEC8E ; Print message: DB 13 DB "Not a CP/M system disc in A",0 CALL ConsoleIn ; Wait for a key; WARM BOOT - RESET jumps to here; ===============================WarmBoot: LD SP,$F4E0 ; Use internal MOS stack EI ; enable INTs (NB! ColdBoot doesn't explictly enable INTs) CALL GetCCP ; Load CCP and BDOS CALL LED32 ; Calculate CCP/BDOS checksum LD HL,LED43 CP (HL) ; Is is same as at previous ColdBoot? JR NZ,LECB0 ; No, jump to complain and reload CALL LED44 ; Initialise things, error handler, esc state, zero page jumps; A consequence of the way the WarmBoot CCP/BDOS validity check works is; that between any two ColdBoots, only the same CCP/BDOS will be recognised; as being valid on reloading. This prevents you being able to, for; instance, soft reboot from a different CCP/BDOS. A ColdBoot is required to; load a different CCP/BDOS in and have it's checksum used.;; Enter CCP; ---------LECE6: LD A,($0004) ; Get current drive CALL SelectTest JP $D403SelectTest: PUSH AF AND $0F LD C,A CALL SelectDisk ; Try to select this drive. POP AF ; Get drive back. LD C,A LD A,H OR L RET NZ ; If valid return C=user+drive. LD C,A ; If not valid, return C=0+0 RET; DiskPatch; (HL+1)=address; (HL+6)=&4B for write, &53 for read; (HL+9)=num OR &20; currDRIVE=drive; currTRACK=track; currBLOCK=block; all registers trashable, return A=resultDiskPatch: LD A,(currDRIVE) ; Get current drive. CP $02 JR NC,HDAccess ; Jump to do hard drive access. PUSH HL LD A,$04 ; Ensure DFS is selected if needed. CALL EnsureFS POP HL PUSH AF ; Save previous filing system. CALL DiskAcc ; Do the disk access POP HL ; Get old filing system back. PUSH AF ; Save result. LD A,H ; Reselect old FS if needed. CALL RestoreFS POP AF ; Return with result. RETHDAccess: LD DE,GBPBchn ; Copy address to GBPBaddr LD BC,9 LDIR ; BC now &0000, HL=>num OR 32 LD (GBPBnum+2),BC ; num=&0000xxxx LD A,(HL) AND $1F LD B,A ; B=num DEC HL DEC HL DEC HL ; HL=>cmd RLD ADD A,A ; &4B/&53 -> &04/&05 -> &08/&0A -> &01/&03 SUB 7 JP DiskPatch2; Calculate checksum of CCP/BDOS; ==============================LED32: XOR A LD HL,BIOS_BOOT-$E00+6 ; Point to start of BDOSLED36: ADD A,(HL) ; Add the byte. INC HL ; step to next EX DE,HL SCF LD HL,BIOS_BOOT-1 SBC HL,DE EX DE,HL ; Have we got past $E9FF yet? JR NZ,LED36 ; Loop back until all done RETLED43: DB $89 ; CCP/BDOS checksum; Various initialisations; =======================LED44: XOR A ; Clear various things LD (LF1E5),A LD (LF1E6),A LD (LF1E8),A LD (LF1E7),A CALL $FFBF ; Ensure RSTERR at $0038 is set up LD HL,($FF84) ; Set default error handler LD ($FFFA),HL LD A,$E5 LD HL,1 CALL OSBYTE ; ESC key returns ASCII LD A,$C3 ; JP opcode LD ($0000),A ; Set RESET and BDOS entries LD ($0005),A LD HL,BIOS_WBOOT ; Point RESET to BIOS WBOOT entry LD ($0001),HL LD HL,BIOS_BOOT-$E00+6 ; Point BDOS to BDOS function entry LD ($0006),HL LD BC,$0080 ; Continue to set DMA to defaut $0080; Set DMA; =======; BC=Disk Memory AddressSetDMA: LD (DMAADDR),BC ; Store current DMA RET; Home Disk; =========DiskHome: LD BC,$0000 ; Prepare TRACK=0 LD A,(LF1E7) OR A JR NZ,SetTrack ; If ???, skip LD (LF1E6),A ; If ???, set ??? to zero; Select track; ============; BC=trackSetTrack: LD A,C LD (TRACK),A ; Store current track RET; Select disk; ===========; C=drive number, E.b0=not first occurance since resetSelectDisk: LD A,C CP $02 ; Test if hard drive present. CALL Z,DiskTest LD HL,0 ; Exit with HL=0 if no hard drive. RET NC LD BC,DPH_Base LD (DRIVE),A ; Store current drive LD L,A ADD HL,HL ; Multiply drive by 16 to index into DPB table ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,BC ; Add base of DPB table RET ; Return HL=DPB for this drive; Select sector; =============; BC=sectorSetSector: LD A,C LD (SECTOR),A ; Store current sector RET; Read a sector from disk; =======================; Read one 128-byte sector specified by DRIVE, TRACK, SECTOR; On return, A=$00 - Ok; A=$01 - Sector error;DiskRead: LD (DISKSP0+1),SP; Use internal stack for disk operations LD SP,$F4CC XOR A LD (LF1E8),A ; LF1E8=$00 LD A,$01 ; $01=DiskRead LD (DSKRDWR),A LD (LF1ED),A LD A,$02 ; Read can be deferred, no pre-read necessary LD (DSKDEFR),A JP LEE2F; Write a sector to disk; ======================; Write one 128-byte sector specified by DRIVE, TRACK, SECTOR; On entry, C=0 - Normal sector write - Write can be deferred; C=1 - Write to directory - Write must be immediate; C=2 - Write to unused sectors - Write can be deferred, no pre-read is necessary; (also set by any read); On return, A=$00 - Ok; A=$01 - Sector error;DiskWrite: LD (DISKSP0+1),SP; Use internal stack for disk operations LD SP,$F4CC XOR A ; $00=DiskWrite LD (DSKRDWR),A LD A,C LD (DSKDEFR),A ; DSKDEFR=write type CP $02 JR NZ,LEDF0 ; No pre-read needed, jump forward; Write can be deferred, no pre-read, writing to unused sectors LD A,$10 LD (LF1E8),A ; LF1E8=$10 LD A,(DRIVE) LD (LF1E9),A ; Copy D/T/S to ... LD A,(TRACK) LD (LF1EA),A LD A,(SECTOR) LD (LF1EB),A; All types of writeLEDF0: LD A,(LF1E8) OR A JR Z,LEE27 ; $00 - jump for ... DEC A LD (LF1E8),A ; <>$00, set to $FF for write LD A,(DRIVE) LD HL,LF1E9 CP (HL) JR NZ,LEE27 ; Different drive LD A,(TRACK) LD HL,LF1EA CP (HL) JR NZ,LEE27 ; Different track LD A,(SECTOR) LD HL,LF1EB CP (HL) JR NZ,LEE27 ; Different sector INC (HL) LD A,(HL) CP $14 ; Last sector on track? JR C,LEE21 LD (HL),$00 ; Set sector back to zero. LD HL,LF1EA INC (HL) ; increment track.LEE21: XOR A LD (LF1ED),A JR LEE2FLEE27: XOR A LD (LF1E8),A INC A LD (LF1ED),A; DiskRead and DiskWrite merge here; ---------------------------------LEE2F: XOR A LD (LF1EC),A; ; Hook here for additional drives by sector access LD A,(SECTOR) OR A RRA OR A RRA LD (BLOCK),A ; BLOCK=SECTOR/4 LD HL,LF1E6 LD A,(HL) LD (HL),$01 OR A JR Z,LEE68 LD A,(DRIVE) LD HL,currDRIVE CP (HL) JR NZ,LEE61 LD A,(TRACK) LD HL,currTRACK CP (HL) JR NZ,LEE61 LD A,(BLOCK) LD HL,currBLOCK CP (HL) JR Z,LEE85 ; Requested block is in deblock area; Requested block not in deblock area; -----------------------------------LEE61: LD A,(LF1E7) OR A CALL NZ,LEF04 ; Flush disk buffer - write it to diskLEE68: LD A,(DRIVE) LD (currDRIVE),A ; Set that requested block is in deblock area LD A,(TRACK) LD (currTRACK),A LD A,(BLOCK) LD (currBLOCK),A LD A,(LF1ED) OR A CALL NZ,LEEC7 ; Read from disk to disk buffer if needed XOR A LD (LF1E7),A; Access data in deblock area; ---------------------------LEE85: LD A,(SECTOR) ; Get sector bottom two bits - 0, 1, 2, 3 AND $03 LD L,A LD H,$00 ADD HL,HL ; Multiply by 128 to index into deblock area ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL ADD HL,HL LD DE,LF272 ADD HL,DE ; HL=Deblock area + 128*block{b0-b1} LD DE,(DMAADDR) LD BC,$0080 ; Prepare to copy 128 bytes - one record LD A,(DSKRDWR) OR A JR NZ,LEEAB ; Skip past if READ LD A,$01 LD (LF1E7),A EX DE,HL ; Swap source and dest for WRITELEEAB: LDIR ; Copy data to/from deblock area LD A,(DSKDEFR) CP $01 ; Was it 'Write to directory'? LD A,(LF1EC)DISKSP0: LD SP,0 ; Restore stack (modified from elsewhere) NOP RET NZ ; Return result if not 'Write to directory' OR A RET NZ ; Return result if error XOR A LD (LF1E7),A CALL LEF04 ; Update directory cache LD A,(LF1EC) RET ; Return result; Read/Write a block to/from deblock area; ---------------------------------------LEEC7: CALL LF02D ; Check if TIME has expired CALL LF077 ; Is requested block within directory? LD A,$53 JR Z,LEF1E ; No, jump past with A=$53 - DSKRD; Access part of the directory; ---------------------------- LD A,(LF1E5) AND A JR Z,LEEE9 ; Directory cache not valid, jump to reload LD A,(LEFC0) LD HL,currDRIVE CP (HL) JR NZ,LEEE9 ; Different drive, load directory LD A,(LEFC7) LD HL,currTRACK CP (HL) JR Z,LEEFF ; Same drive and track, cache still valid, use itLEEE9: LD HL,LEFC0 ; Point to control block to load directory to cache LD A,(currDRIVE) LD (HL),A ; Set drive in control block LD A,(currTRACK) LD (LEFC7),A ; Set track in control block CALL LEF49 ; Clear cache and read new directory RET NZ LD A,$FF LD (LF1E5),A ; Set directory data validLEEFF: LD A,$01 JP LEFFF ; Jump to read data from cached directory; Update directory cache after writing to disk buffer; ---------------------------------------------------LEF04: CALL LF02D ; Check if directory cache has expired LD A,$4B CALL LEF1E ; Do 'DiskWrite' JR NZ,LEF17 ; Error occured, invalidate cache CALL LF077 RET Z ; Return if not directory data LD A,$00 JP LEFFF ; Also write to cached dataLEF17: PUSH AF XOR A LD (LF1E5),A ; Set cache has expired POP AF RET; Read/Write data area; --------------------LEF1E: LD (LEFBB),A ; Store disk access command LD A,(currDRIVE) LD (LEFB5),A ; Store drive in control block LD A,(currTRACK) CP $50 JR C,LEF3A ; Track<80, side 0, jump forward LD C,A LD A,(LEFB5) ADD A,$02 ; Add 2 to drive for side 1 LD (LEFB5),A LD A,$9F SUB C ; Convert track 80-159 to 79-0LEF3A: LD (LEFBC),A ; Store track in control block LD A,(currBLOCK) CALL LEFAC LD (LEFBD),A LD HL,LEFB5LEF49: PUSH HL CALL LF063 ; Set previous TIME=current TIME POP HL; Return here to retry; Hook here to add extra drives by block accessLEF4E: CALL DiskPatch ; Disk Access, HL=>control block AND A JR Z,LEFA7 ; Ok, jump to update flag and return CP $12 JR NZ,LEFA5 ; Not 'DiskReadOnly', jump to return disk error PUSH HL CALL LEC8E DB 13, "Bdos Err On ", 0LEF6A: LD A,(currDRIVE) AND $01 ADD A,'A' LD C,A CALL ConsoleOut ; Print drive letter CALL LEC8E DB ": R/O (Disc is Write Protected)", 0 CALL ConsoleIn ; Wait for a keypress POP HL CP 'R' JR Z,LEF4E ; 'R'etry CP 'r' JR Z,LEF4E ; 'r'etry RST $00 ; Not Retry, rebootLEFA5: LD A,$01LEFA7: AND ALEFA8: LD (LF1EC),A RET; Convert BLOCK 0-4 to sector 0/4/8/2/6; -------------------------------------LEFAC: LD C,A LD B,$00 LD HL,InterLeaveTab ADD HL,BC LD A,(HL) RET; OSWORD $7F control block; ------------------------LEFB5: DB $00 ; Drive/SideLEFB6: DW LF272 ; Data address DW 0 DB $03LEFBB: DB $53 ; CommandLEFBC: DB $28 ; TrackLEFBD: DB $00 ; Sector DB $22 ; 2x256-byte sectors DB $00 ; Result; OSWORD $7F control block to access directory cache; --------------------------------------------------LEFC0: DB $00 ; Drive/Side DW $2600 ; Data address in directory cache DW $FFFF DB $03 DB $53 ; CommandLEFC7: DB $03 ; Track DB $00 ; Sector DB $2A ; 10x256-byte sectorsLEFCA: DB $00 ; Result; XXXInterLeaveTab: DB 0 ; Sector interleave table DB 4 DB 8 DB 2 DB 6; Continue to look for hard drive; -------------------------------DiskTest: XOR A ; Prepare 'no disk access'DiskPatch2: LD (GBPBnum+0),BC LD B,A ; num=&0000nn00, B=0 test, =1 write, =3 read LD DE,(currTRACK) LD D,C ; D=0, E=TRACK, B=cmd, C=0, HL=xxx LD A,(currBLOCK) ADD A,A ADD A,A ; A=BLOCK moved up to b7 SRL E RRA LD H,A LD L,C ; DE=<0-TRACK>, A=<Tb0-BLOCK-0> -> DE=&00<TRK>, HL=<BLK><00> INC H JR NZ,DiskNoInc INC DE ; DEHL=<Tb0-BLOCK-0><00000000>+256 - index into MyZ80 disk imageDiskNoInc: LD (GBPBptr+0),HL LD (GBPBptr+2),DE; LD A,(DriveCfs) CALL EnsureFS ; Select DriveC filing system if needed PUSH AF ; Save previous FS, LD C,$FF ; prepare C=&FF for not found (no drive present) LD HL,DriveC JP DiskPatch3;; Read or write data to/from directory cache in I/O memory; --------------------------------------------------------LEFFF: LD (LF02C),A ; Store read/write command in control block LD A,(currBLOCK) CALL LEFAC ; Convert BLOCK to sector number LD HL,$2500 ; HL=cache-256 INC A LD B,A ; B=sector+1 LD DE,$0100LF010: ADD HL,DE ; Index into cache at $2600+sector*256 DJNZ LF010 LD (LF022),HL LD HL,LF020 ; Store in control block LD A,$FF CALL OSWORD ; Make transfer. XOR A RET; OSWORD $FF control block - read/write I/O memory; ------------------------------------------------LF020: DB $0D ; Send block length=13 DB $01 ; Receive block length=1LF022: DW $2600 ; I/O transfer address DW $0000 DW LF272 DW $0000 ; CoPro transfer address DW $0200 ; Data lengthLF02C: DB $01 ; Transfer type 0=read from I/O, 1=write to I/O; Compare current TIME to previous TIME+3.84s; -------------------------------------------LF02D: LD A,$01 LD HL,LF472 CALL OSWORD ; Read current TIME LD DE,(LF477+0) LD HL,(LF472+0) ; Compare current TIME to stored TIME OR A SBC HL,DE RR B LD DE,$0180 OR A SBC HL,DE JR NC,LF05E LD DE,(LF477+2) LD HL,(LF472+2) RL B SBC HL,DE JR NZ,LF05E LD HL,(LF477+4) LD A,(LF472+4) SBC A,L RET ZLF05E: XOR A ; TIME has expired LD (LF1E5),A RETLF063: LD A,(currDRIVE) ; Compare with directory drive LD HL,LEFC0 CP (HL) RET NZ ; Different drives, return LD HL,LF472 ; Copy currTIME to prevTIME LD DE,LF477 LD BC,$0005 LDIR RET; Check if requested block is within the directory; ------------------------------------------------LF077: LD A,(currTRACK) ; Check requested TRACK CP $03 ; Track 3 - directory, exit with A=$FF JR Z,LF08B CP $04 JR NZ,LF089 ; Not track 4 - exit with A=$00 LD A,(currBLOCK) ; Check requested block CP $03 JR C,LF08B ; Block<3 - directory, exit with A=$FFLF089: XOR A ; A=$00, not directory RETLF08B: XOR A DEC A ; A=$FF, directory RET; Check for any boot files; ------------------------LF08E: LD C,$0E CALL $DC06 ; Reset disk system LD DE,strBOOT ; Search for "BOOT.COM" LD C,$11 CALL $DC06 INC A JR Z,LF0BF ; Not found, try next one CALL LEC8E ; Print message: DB "Running BOOT.COM",13,0 LD HL,cmdBOOT ; Point to "BOOT" command stringLF0B6: LD DE,$D407 LD BC,$000D ; Copy command into CCP command buffer LDIR RETLF0BF: LD DE,subBOOT LD C,$11 CALL $DC06 ; Search for "BOOT.SUB" INC A RET Z ; Not there, exit LD DE,strSUBMIT ; Search for "SUBMIT.COM" LD C,$11 CALL $DC06 INC A JR Z,LF0F1 ; Not there, report the problem - if BOOT.SUB is present, SUBMIT.COM needs to also be there CALL LEC8E ; Print message DB "Submitting BOOT.SUB",13,0LF0EC: LD HL,cmdSUBMIT JR LF0B6 ; Jump to copy "SUBMIT BOOT" into CCP command bufferLF0F1:; CALL LEC8E ; Print message:; DB "No SUBMIT.COM",0 RETcmdBOOT: DB 4, "BOOT", 0cmdSUBMIT: DB 11, "SUBMIT BOOT", 0strSUBMIT: DB 0, "SUBMIT COM", 0subBOOT: DB 0, "BOOT SUB", 0 ; Will be overwritten when SUMBIT.COM searched forstrBOOT: DB 0, "BOOT COM", 0 ; Will be overwritten when BOOT.SUB searched for;; F53B - Start of core MOS code; F800 - Start of Z80Tube MOS code; COLD BOOT - Entered on startup after booting from Reset; =======================================================ColdBoot: LD SP,$F4E0 ; Use internal MOS stackLF167 EQU ColdBoot+20LF168 EQU ColdBoot+21 CALL LED32 ; Get 8-bit checksum of CCP/BDOS in memory LD (LED43),A CALL LED44 ; Initialise things, error handler, esc state, zero page jumps CALL LEA93 ; Initialise input stream and printer ignore state LD HL,($EA04) LD ($EA01),HL ; ColdBoot now goes to WarmBoot LD HL,alvC_Z80Tube+256 ; DriveC allocation vector for Z80Tube LD A,(HL) ; Check for Z80Tube at &F800 DEC H CP $C3 JR Z,ColdALV ; Use Z80Tube-256 as alvC LD HL,alvC_CoPro ; DriveC allocation vector for hardware CoProColdALV: LD (DPH_C+14),HL ; Set up DriveC allocation vector CALL LEC8E ; Print startup message DB 13,"Acorn CP/M 2.2 - HDBIOS 1.24",13,0 LD C,13 CALL $DC06 ; Reset disks LD A,2 CALL SelectTest ; See if drive C present, C=2 or 0 PUSH BC LD A,C LD (4),A ; Set system drive LD E,C CALL LF08E ; Check for boot files on this drive POP BC JP $D400 ; Enter CCP with C=driveDiskPatch3: LD A,(HL) CP '!' ; Is filename present? JR C,NoImageFile ; No image filename LD A,$C0 CALL OSFIND ; Open hard drive image file AND A JR Z,NoImageFile ; Skip past if no image file LD HL,GBPBchn ; Point to control block LD (HL),A ; set channel PUSH AF LD A,B AND A CALL NZ,OSGBPB ; do disk data transfer if nonzero POP HL XOR A LD C,A CALL OSFIND ; Close channel, C=&00 for OkNoImageFile: POP AF CALL RestoreFS ; Reselect oldFS if needed LD A,B AND A LD A,C RET NZ ; If disk access done, return A=&00/&FF CP $FF LD A,2 RET ; If disk test done, return A=2 (drive C), C=ok, NC=absent;RestoreFS: AND A LD H,A JR NZ,SelectFS ; A<>&00, select FSEnsureFS: AND A RET Z ; Nothing to reselect LD H,A XOR A LD E,A CALL OSARGS ; A=FS, wantedFS in H preserved XOR H RET Z XOR H ; If currentFS=wantedFS, return with A=0SelectFS: PUSH AF LD L,18 LD A,143 ; Select FS in H CALL OSBYTE POP AF RET ; Return previous FS or &00 in A; Sector Translate; ================; On entry, BC=sector; On exit, HL=translated sector;SectorTranslate: LD H,B LD L,C RET ; No translation;GetCCP: LD A,63 ; Ask for a CCP with *fx63 CALL OSBYTE INC L RET NZ JP LoadCCP ; Otherwise, use Z80MOS org $f1e0 DB "HDBIOS 1.24 (12 Sep 2010) (C)JGH"DRIVE EQU $F1DE ; Current driveTRACK EQU DRIVE+1 ; Current trackSECTOR EQU TRACK+1 ; Current sectorcurrDRIVE: EQU SECTOR+1currTRACK: EQU currDRIVE+1currBLOCK: EQU currTRACK+1BLOCK: EQU currBLOCK+1LF1E5: EQU BLOCK+1 ; $00 - TIME has expired, $FF - dir cache still validLF1E6: EQU LF1E5+1 ; Used in HOMELF1E7: EQU LF1E6+1 ; Used in HOMELF1E8: EQU LF1E7+1LF1E9: EQU LF1E8+1 ; DRIVELF1EA: EQU LF1E9+1 ; TRACKLF1EB: EQU LF1EA+1 ; SECTORLF1EC: EQU LF1EB+1 ; READ/WRITE resultLF1ED: EQU LF1EC+1DSKRDWR: EQU LF1ED+1DSKDEFR: EQU DSKRDWR+1 ; Disk write deferredDMAADDR: EQU DSKDEFR+1 ; DMALF1F2: EQU $F1F2 ; DIRBUF, 128 bytes.LF272: EQU LF1F2+128 ; Disk deblock areaLF472: EQU LF272+512 ; Control block to read TIMELF477: EQU LF472+5 ; Previously read TIMELF47C EQU LF477+5; F4CC - internal stack for Disk routines, 40 entries; F4E0 - internal stack for Boot, 9 entries
Statistics: Posted by Coeus — Thu Feb 15, 2024 3:37 am