REM > BIOS120/src REM Acorn Z80 V1 CP/M BIOS : A%=0:X%=1:os%=(USR&FFF4 AND &FF00)DIV256:IFos%=6 AND PAGE>&8000:PRINT"Running Z80...":SYS "OS_GetEnv" TO A$:OSCLI"TextToBas "+MID$(A$,INSTR(A$," ",1+INSTR(A$," ")))+" -crunch *Z80":END quit%=?&80<>0:?&80=0 ON ERROR REPORT:PRINT" at line ";ERL:END : DIM mcode% &C00:start%=&EA00:ver$="1.20":name$="BIOS120/SYS" : REM MOS Entries: OSRDCH=&FFE0:OSWRCH=&FFEE:OSWORD=&FFF1:OSBYTE=&FFF4 : FOR P=0 TO 1 P%=start%:O%=mcode% [OPT P*3+4 .BIOS_BOOT :JP ColdBoot .BIOS_WBOOT :JP WarmBoot .BIOS_CONST :JP ConsoleStatus ; CON .BIOS_CONIN :JP ConsoleIn ; CON .BIOS_CONOUT :JP ConsoleOut ; CON .BIOS_LIST :JP ListOut ; LST (printer) .BIOS_PUNCH :JP PunchOut ; PUN .BIOS_READER :JP ReaderIn ; RDR .BIOS_HOME :JP DiskHome .BIOS_SELDSK :JP SelectDisk .BIOS_SETTRK :JP SetTrack .BIOS_SETSEC :JP SetSector .BIOS_SETDMA :JP SetDMA .BIOS_READ :JP DiskRead .BIOS_WRITE :JP DiskWrite .BIOS_LISTST :JP ListStatus ; LST (printer) .BIOS_SECTRAN :JP SectorTranslate : .PatchArea RET : DEFM "Greetings from " DEFM "Clean End - Ian Mark Neil Simon " DEFM "Dirty End - Dave Ian John Toby" DEFM "Boss Man - Howard " : .LEA93 ; Reset input stream LD A,&83 ; Specify CON=UC1 RDR=TTY PUN=TTY LST=LPT .LEA95 LD (&0003),A ; Reset IOBYTE LD L,&02:LD A,&02:CALL OSBYTE ; Input stream=kbd, serial enabled JP LEBBD : .ConsoleStatus CALL LEB02:DEFB &81 ; IOBYTE b0-1, test TTY/CRT/BAT/UC1 DEFW LEB8A:DEFW LEB50:DEFW ReaderStatus:DEFW LEB24 : .ConsoleIn CALL LEB02:DEFB &01 ; IOBYTE b0-b1, input from TTY/CRT/BAT/UC1 DEFW LEB98:DEFW LEB54:DEFW ReaderIn:DEFW OSRDCH : .ConsoleOut CALL LEB02:DEFB &01 ; IOBYTE b0-b1, output to TTY/CRT/BAT/UC1 DEFW TTYOut:DEFW CRTOut:DEFW ListOut:DEFW UC1Out : .ListOut CALL LEB02:DEFB &03 ; IOBYTE b6-7, output to TTY/CRT/LPT/UL1 DEFW TTYOut:DEFW CRTOut:DEFW LEBA6:DEFW LEBA9 : .ListStatus CALL LEB02:DEFB &03 ; IOBYTE b6-7, test TTY/CRT/LPT/UL1 DEFW LEBA1:DEFW LEB93:DEFW LEBC4:DEFW LEBC4 : .PunchOut CALL LEB02:DEFB &05 ; IOBYTE b4-5, output to TTY/PTP/UP1/UP2 DEFW TTYOut:DEFW CRTOut:DEFW LEB23:DEFW LEB23 : .ReaderIn CALL LEB02:DEFB &07 ; IOBYTE b2-3, input from TTY/PTR/UR1/UR2 DEFW LEB98:DEFW LEB54:DEFW LEB21:DEFW LEB21 : .ReaderStatus CALL LEB02:DEFB &07 ; IOBYTE b2-3, test TTY/PTR/UR1/UR2 DEFW LEB8A:DEFW LEB50:DEFW LEB95:DEFW LEB95 : ; 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 flag .LEB10 LD A,(&0003) ; Get IOBYTE .LEB13 RLCA:DJNZ LEB13 ; Rotate left by mask flag AND &06 LD D,&00:LD E,A:ADD HL,DE ; Index into four inline addresses LD E,(HL):INC HL:LD D,(HL) ; Jump to the inline address specified by EX DE,HL:JP (HL) ; IOBYTE rotated by the flag byte : ; Null input - return EOF ; ----------------------- .LEB21 LD A,&1A : ; Null output - sink ; ------------------ .LEB23 RET : ; Read UC1 status ; =============== .LEB24 LD HL,LF167:XOR A ; Test flag OR (HL):JR Z,LEB2E ; If zero, check current input stream DEC (HL) ; Decrement flag XOR A:RET ; Return &00 : .LEB2E LD HL,&FF00:LD A,&B1:CALL OSBYTE ; Read input stream .LEB36 CALL LEB8C:RET NZ LD A,&D8:LD HL,&FF00:CALL OSBYTE ; Read soft key length LD A,L:AND A:JR NZ,LEB95 LD HL,LF167:LD (HL),&0C ; Set flag to 12 RET : ; Output to UC1 - RDCH/WRCH ; ========================= .UC1Out LD A,C:JP &FF9E ; Send via PROUT to WRCH or TERMOUT : ; Read CRT status ; =============== .LEB50 LD L,&00:JR LEB36 ; Jump to test buffer 0 (keyboard) : ; CRT input (ie, KBD/VDU) ; ======================= .LEB54 LD L,&02 ; Keyboard Input, Serial Enabled .LEB56 LD A,&02:CALL OSBYTE ; Set input stream, L=previous stream 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 input .LEB65 LD A,&02:CALL OSBYTE ; Set input stream POP AF:RET ; Return with character read : ; CRT Output - VDU/KBD ; ==================== .CRTOut LD HL,&00F4 ; Output=*,noprint,*,nospool,printer,novduprinter,vdu,noserial .LEB6F ; Enter here with HL=*FX3 value LD A,&03:CALL OSBYTE ; Select output stream PUSH HL ; Save previous output stream XOR A:CALL &FFC8 ; Switch off terminal mode PUSH AF ; Remember previous state LD A,C:CALL OSWRCH ; Output the character POP AF:CALL &FFC8 ; Restore previous terminal state POP HL:LD H,&00 ; Get previous output stream LD A,&03:JP OSBYTE ; Restore previous output and return : ; Read TTY status ; =============== .LEB8A LD L,&01 ; Serial Input buffer .LEB8C LD A,&98:CALL OSBYTE ; Examine buffer JR NC,LEB95 ; Buffer not empty .LEB93 XOR A:RET ; Return A=&00 if empty .LEB95 XOR A:DEC A:RET ; Return A=&FF if not empty : ; Console Input ; ============= .LEB98 LD L,&01:JR LEB56 ; Jump to read from Serial Input : ; TTY Output - serial output ; ========================== .TTYOut LD HL,&00F7:JR LEB6F ; Output=*,noprint,*,nospool,printer,novduprinter,novdu,serial : .LEBA1 LD HL,&FFFD:JR LEBC7 ; Jump to check SerialOut buffer with HL=-3 : ; LPT Output - test printer before outputing ; ========================================== .LEBA6 CALL LEBD2 ; Test printer status : ; UL1 Output - output straight to printer ; ======================================= .LEBA9 LD A,C:AND A ; Test if C=0 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:CALL OSBYTE ; Set printer ignore char to &FF so can output &00 LD HL,&001A:CALL LEB6F ; Write character to printer .LEBBD LD A,&06:LD L,&00:JP OSBYTE ; Set printer ignore char to &00 : .LEBC4 LD HL,&FFFC ; HL=-4 - printer buffer .LEBC7 LD A,&80:CALL OSBYTE ; Check buffer status LD A,L:AND A:RET Z ; Return &00 if no space left (bug, should be AND H) XOR A:DEC A:RET ; Return &FF if space available (bug, don't need XOR A) : ; Test printer status ; =================== .LEBD2 LD A,(&0003):AND &C3 ; Get IOBYTE LST and CON fields CP &82:RET Z ; If LST=LPT, CON=BAT, exit CALL LEC80:RET NZ ; Check and return if printer available LD A,&86:CALL OSBYTE ; Read POS and VPOS LD (LF168),HL LD A,&87:CALL OSBYTE:LD A,H ; Read MODE 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 00x00 .LEBFB LD H,&18 ; H=25-1 lines .LEBFD PUSH HL:CALL LEC6C ; PRINT TAB(54,Y); or (0,0); CALL LEC8E DEFM "Printer off line":DEFB 0 CALL LEC80 POP HL:PUSH HL ; Get coord back CALL LEC6C ; PRINT TAB(54,Y); or (0,0); CALL LEC8E DEFM "SPACE starts Printer Sink":DEFB 0 .LEC3A CALL LEBC4:JR NZ,LEC5A CALL ConsoleStatus AND A:JR Z,LEC3A CALL ConsoleIn CP ASC" ":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 sink .LEC5A POP HL:CALL LEC6C ; Get coords and PRINT TAB(54,Y); or (0,0); LD B,&19 ; Print 25 spaces to overwrite message .LEC60 PUSH BC LD C,ASC" ":CALL ConsoleOut ; Print a space POP BC:DJNZ LEC60 LD HL,(LF168) ; Get original POS/VPOS back .LEC6C PUSH BC:PUSH HL ; Save HL and BC LD C,&1F:CALL ConsoleOut ; VDU 31 - TAB POP HL:PUSH HL ; Get X coord LD C,L:CALL ConsoleOut ; Send X coord POP HL ; Get Y coord LD C,H:CALL ConsoleOut ; Send Y coord POP BC:RET ; Restore BC : ; Test if output to printer online ; ================================ .LEC80 LD DE,&AFC8 ; Test 45,000 times .LEC83 CALL LEBC4:RET NZ ; Test printer buffer, 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:PUSH BC:PUSH AF ; Save everything else .LEC92 LD A,(HL):INC HL ; Get a byte, 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 next .LEC9E POP AF:POP BC:POP DE ; Restore everything EX (SP),HL:RET ; Stack pointer and return to it : ; CON_ASCII ; ========= .LECA3 CP &0D:JR NZ,LECAC ; Jump to output non- CALL LECAC:LD A,&0A ; Print and add a .LECAC LD C,A:JP ConsoleOut ; Print character : ; Complain about disk being booted from .LECB0 CALL LEC8E ; Print message: DEFB 13:DEFM "Not a CP/M system disc in A":DEFB 0 CALL ConsoleIn ; Wait for a key : ; WARM BOOT - RESET jumps to here ; =============================== .WarmBoot LD SP,&F4E0:EI ; Use internal MOS stack, enable INTs ; NB! ColdBoot doesn't explictly enable INTs CALL &FFA7 ; 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):LD C,A:AND &0F ; Get current drive CP &02:JR C,LECF4 ; If supported drive, use it LD A,C:AND &F0:LD C,A ; Force drive to 0 .LECF4 JP &D400 ; Enter CCP with C=drive/user : ; COLD BOOT - Entered on startup after booting from Reset ; ======================================================= .ColdBoot LD SP,&F4E0 ; Use internal MOS stack CALL LED32:LD (LED43),A ; Get 8-bit checksum of CCP/BDOS in memory XOR A:LD (&0004),A ; Clear Current Drive+User CALL LED44 ; Initialise things, error handler, esc state, ; zero page jumps CALL PatchArea ; Call any patch code CALL LEA93 ; Initialise input stream and printer ignore state CALL LEC8E ; Print startup message: DEFB 13:DEFM "Acorn CP/M 2.2 - Bios 1.20":DEFB 13:DEFB 0 CALL LF08E:JR LECE6 ; Look for any boot file, enter CCP : ; Calculate checksum of CCP/BDOS ; ============================== .LED32 XOR A:LD HL,BIOS_BOOT-&E00+6 ; Point to start of BDOS .LED36 ADD A,(HL):INC HL ; Add the byte, 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:RET ; Loop back until all done .LED43 DEFB &00 ; 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):LD (&FFFA),HL ; Set default error handler LD A,&E5:LD HL,1:CALL OSBYTE ; ESC key returns ASCII LD A,&C3 ; JP opcode LD (&0000),A:LD (&0005),A ; Set RESET and BDOS entries 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 Address .SetDMA LD (DMAADDR),BC:RET ; Store current DMA : ; 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=track .SetTrack LD A,C:LD (TRACK),A:RET ; Store current track : ; Sector Translate ; ================ ; On entry, BC=sector ; On exit, HL=translated sector ; .SectorTranslate LD H,B:LD L,C:RET ; No translation : ; Select disk ; =========== ; C=drive number, E.b0=not first occurance since reset .SelectDisk LD HL,&0000 ; HL=0 for no drive available LD A,C:CP &02:RET NC ; Only drive 0, 1 supported LD BC,DPH_Base:LD (DRIVE),A ; Store current drive LD L,A:ADD HL,HL:ADD HL,HL ; Multiply drive by 16 to index into DPB table ADD HL,HL:ADD HL,HL:ADD HL,BC ; Add base of DPB table RET ; Return HL=DPB for this drive : ; Select sector ; ============= ; BC=sector .SetSector LD A,C:LD (SECTOR),A:RET ; Store current sector : ; 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 (DISKSP),SP:LD SP,&F4CC ; Use internal stack for disk operations XOR A:LD (LF1E8),A ; LF1E8=&00 LD A,&01:LD (DSKRDWR),A ; &01=DiskRead LD (LF1ED),A LD A,&02:LD (DSKDEFR),A ; Read can be deferred, no pre-read necessary 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 (DISKSP),SP:LD SP,&F4CC ; Use internal stack for disk operations XOR A:LD (DSKRDWR),A ; &00=DiskWrite 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 write .LEDF0 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:JR C,LEE21 LD (HL),&00 LD HL,LF1EA:INC (HL) .LEE21 XOR A:LD (LF1ED),A JR LEE2F .LEE27 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 disk .LEE68 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):AND &03 ; Get sector bottom two bits - 0, 1, 2, 3 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 WRITE .LEEAB LDIR ; Copy data to/from deblock area LD A,(DSKDEFR):CP &01 ; Was it 'Write to directory'? LD A,(LF1EC):LD SP,(DISKSP) ; Restore stack 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 still valid, jump to... 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 it .LEEE9 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:RET NZ ; Clear cache and read new directory LD A,&FF:LD (LF1E5),A ; Set directory data valid .LEEFF 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 data : .LEF17 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:LD (LEFB5),A ; Add 2 to drive for side 1 LD A,&9F:SUB C ; Convert track 80-159 to 79-0 .LEF3A LD (LEFBC),A ; Store track in control block LD A,(currBLOCK):CALL LEFAC LD (LEFBD),A LD HL,LEFB5 .LEF49 PUSH HL:CALL LF063:POP HL ; Set previous TIME=current TIME .LEF4E ; Return here to retry ; ; Hook here to add extra drives by block access CALL &FFA4 ; 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 DEFB 13:DEFM "Bdos Err On ":DEFB 0 .LEF6A LD A,(currDRIVE) AND &01:ADD A,ASC"A" LD C,A:CALL ConsoleOut ; Print drive letter CALL LEC8E DEFM ": R/O (Disc is Write Protected)":DEFB 0 CALL ConsoleIn:POP HL ; Wait for a keypress CP ASC"R":JR Z,LEF4E ; 'R'etry CP ASC"r":JR Z,LEF4E ; 'r'etry RST &00 ; Not Retry, reboot .LEFA5 LD A,&01 .LEFA7 AND A .LEFA8 LD (LF1EC),A:RET : ; Convert BLOCK 0-4 to sector 0/4/8/2/6 ; ------------------------------------- .LEFAC LD C,A:LD B,&00:LD HL,LEFEB ADD HL,BC:LD A,(HL):RET : ; OSWORD &7F control block ; ------------------------ .LEFB5:DEFB &00 ; Drive/Side .LEFB6:DEFW LF272:DEFW 0 ; Data address DEFB &03 .LEFBB:DEFB &53 ; Command .LEFBC:DEFB &00 ; Track .LEFBD:DEFB &00 ; Sector DEFB &22 ; 2x256-byte sectors DEFB &00 ; Result : ; OSWORD &7F control block to access directory cache ; -------------------------------------------------- .LEFC0:DEFB &00 ; Drive/Side DEFW &2600:DEFW &FFFF ; Data address in directory cache DEFB &03 DEFB &53 ; Command .LEFC7:DEFB &00 ; Track DEFB &00 ; Sector DEFB &2A ; 10x256-byte sectors .LEFCA:DEFB &00 ; Result : .DPH_Base DEFW &0000:DEFW &0000:DEFW &0000:DEFW &0000 ; DPH for drive 0 DEFW LF1F2:DEFW DPB_Acorn400k:DEFW LF16A:DEFW LF1AA : DEFW &0000:DEFW &0000:DEFW &0000:DEFW &0000 ; DPH for drive 1 DEFW LF1F2:DEFW DPB_Acorn400k:DEFW LF18A:DEFW LF1C3 : .LEFEB ; Sector interleave table DEFB 0:DEFB 4:DEFB 8:DEFB 2:DEFB 6 : .DPB_Acorn400k ; DPB for AcornCPM 400k disk DEFW &0014 ; SPT=20 Sectors Per Track DEFB &04 ; BSH=4 Block Shift DEFB &0F ; BLM=15 Block Mask DEFB &01 ; EXM=1 Extent Mask DEFW &00C3 ; DSM=195 Disk Sector Max DEFW &007F ; DRM=127 Maximum directory entry number DEFB &C0 ; AL0=&C0 Directory occupies DEFB &00 ; AL1=&00 first two blocks DEFW &0020 ; CKS=32 Size of directory checksum vector DEFW &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 : ; 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:INC A:LD B,A ; HL=cache-256, B=sector+1 LD DE,&0100 .LF010 ADD HL,DE:DJNZ LF010 ; Index into cache at &2600+sector*256 LD (LF022),HL:LD HL,LF020 ; Store in control block LD A,&FF:CALL OSWORD:XOR A:RET ; Make transfer and return : ; OSWORD &FF control block - read/write I/O memory ; ------------------------------------------------ .LF020 DEFB &0D ; Send block length=13 DEFB &01 ; Receive block length=1 .LF022 DEFW &0000:DEFW &0000 ; I/O transfer address DEFW LF272:DEFW &0000 ; CoPro transfer address DEFW &0200 ; Data length .LF02C DEFB &00 ; 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 Z .LF05E XOR A:LD (LF1E5),A:RET ; TIME has expired : .LF063 LD A,(currDRIVE):LD HL,LEFC0 ; Compare with directory drive CP (HL):RET NZ ; Different drives, return LD HL,LF472:LD DE,LF477 ; Copy currTIME to prevTIME LD BC,&0005:LDIR:RET : ; Check if requested block is within the directory ; ------------------------------------------------ .LF077 LD A,(currTRACK) ; Check requested TRACK CP &03:JR Z,LF08B ; Track 3 - directory, exit with A=&FF 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=&FF .LF089 XOR A:RET ; A=&00, not directory .LF08B XOR A:DEC A:RET ; A=&FF, directory : ; Check for any boot files ; ------------------------ .LF08E LD C,&0D:CALL &DC06 ; Reset disk system LD DE,LF146:LD C,&11:CALL &DC06 ; Search for "BOOT.COM" INC A:JR Z,LF0BF ; Not found, try next one CALL LEC8E ; Print message: DEFM "Running BOOT.COM":DEFB 13:DEFB 0 LD HL,LF119 ; Point to "BOOT" command string .LF0B6 LD DE,&D407:LD BC,&000D ; Copy command into CCP command buffer LDIR:RET : .LF0BF LD DE,LF139:LD C,&11:CALL &DC06 ; Search for "BOOT.SUB" INC A:RET Z ; Not there, exit LD DE,LF12C:LD C,&11:CALL &DC06 ; Search for "SUBMIT.COM" 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 DEFM "Submitting BOOT.SUB":DEFB 13:DEFB 0 .LF0EC LD HL,LF11F:JR LF0B6 ; Jump to copy "SUBMIT BOOT" into CCP command buffer : .LF0F1 CALL LEC8E ; Print message: DEFM "BOOT.SUB present but no SUBMIT.COM":DEFB 13:DEFB 0 RET : .LF119 DEFB 4:DEFM "BOOT":DEFB 0 .LF11F DEFB 11:DEFM "SUBMIT BOOT":DEFB 0 .LF12C DEFB 0:DEFM "SUBMIT COM":DEFB 0 .LF139 DEFB 0:DEFM "BOOT SUB":DEFB 0 ; Will be overwritten when SUMBIT.COM searched for .LF146 DEFB 0:DEFM "BOOT COM":DEFB 0 ; Will be overwritten when BOOT.SUB searched for ; ; All from here onwards overwritten while code is executing ; --------------------------------------------------------- ; .LF153 NOP:NOP:NOP ; S1,S2,RC for BOOT.COM NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP ; Allocation vector for BOOT.COM NOP:NOP:NOP:NOP:NOP:NOP:NOP:NOP ; Allocation vector for BOOT.COM NOP ; CR for BOOT.COM .LF167 :NOP ; Used in CONST ; R0 for BOOT.COM .LF168 :DEFW &0000 ; POS, VPOS ; R1,R2 for BOOT.COM .LF16A :DEFM STRING$(&20,CHR$0) ; CSV for drive 0 .LF18A :DEFM STRING$(&20,CHR$0) ; CSV for drive 1 .LF1AA :DEFM STRING$(&19,CHR$0) ; ALV for drive 0 .LF1C3 :DEFM STRING$(&19,CHR$0) ; ALV for drive 1 .DISKSP ; &F1DC :DEFW &0000 ; Disk routines SP store .DRIVE ; &F1DE :NOP ; Current drive .TRACK ; &F1DF :NOP ; Current track ; ; All up to this point written to system tracks ; --------------------------------------------- ; .SECTOR ; &F1E0 :NOP ; Current sector .currDRIVE ; &F1E1 :NOP ; Block in deblock area .currTRACK ; &F1E2 :NOP .currBLOCK ; &F1E3 :NOP .BLOCK ; &F1E4 :NOP .LF1E5 :NOP ; &00 - TIME has expired, &FF - dir cache still valid .LF1E6 :NOP ; Used in HOME .LF1E7 :NOP ; Used in HOME .LF1E8 :NOP .LF1E9 :NOP ; DRIVE .LF1EA :NOP ; TRACK .LF1EB :NOP ; SECTOR .LF1EC :NOP ; READ/WRITE result .LF1ED :NOP .DSKRDWR ; &F1EE :NOP .DSKDEFR ; &F1EF :NOP ; Disk write deferred .DMAADDR ; &F1F0 :DEFW &0000 ; DMA .LF1F2 :DEFM STRING$(128,CHR$0) ; DIRBUF .LF272 :DEFM STRING$(128,CHR$0) ; Disk deblock area :DEFM STRING$(128,CHR$0) :DEFM STRING$(128,CHR$0) :DEFM STRING$(128,CHR$0) .LF472 :DEFM STRING$(5,CHR$0) ; Control block to read TIME .LF477 :DEFM STRING$(5,CHR$0) ; Previously read TIME .LF47C ; F4CC - internal stack for Disk routines, 40 entries ; F4E0 - internal stack for Boot, 9 entries ; F53B - Start of core MOS code ; F800 - Start of Z80Tube MOS code ]:NEXT : A$="*Save "+name$+" "+STR$~mcode%+"+800 "+STR$~start%+" "+STR$~start% PRINT " "A$:IF quit%:OSCLI A$:*Quit