; Tiny ROM-able CP/M BIOS ; J.G.Harston 08-Oct-2006 ; ======================= ; This fits a tiny BIOS and the standard &E00-byte BDOS into ; a 4K ROM at the top of memory. The ROM must be paged into ; all memory on RESET. RAM must be writable through the ROM. ; The ROM must be paged out and RAM readable after any I/O access. ; I/O is implmented with a 6850 ACIA for serial console access ; and an IDE interface for disk access. ; The memory map is as follows: ; E800 CCP - loaded into RAM from 'CCP.SYS' on disk ; F000 BDOS - copied from ROM on startup ; FE00 BIOS - copied from ROM on startup ; The file loaded on boot could well be a bootstrap that replaces the ; ROM code and loads a fuller system. ; MEMORY ; ------ RESET EQU &0000 ; Reset entry point BDOS EQU &0005 ; BDOS entry point FCB EQU &005C ; Default FCB BDOS0 EQU &F000 ; BDOS start BIOS0 EQU &FE00 ; BIOS start CCP EQU BDOS0-&800 ; CCP loaded here ; I/O ; --- STATUS EQU 0 ; 6850 status register CONTROL EQU 0 ; 6850 control register DATA EQU 1 ; 6850 data register IDE EQU 8 ; IDE registers IDE_DATA EQU IDE+0 IDE_COUNT EQU IDE+2 IDE_SEC EQU IDE+3 IDE_CYL EQU IDE+4 IDE_HEAD EQU IDE+6 IDE_CMD EQU IDE+7 ORG BDOS0 ; Start of ROM JP BOOT ; This is at location &0000 on reset DEFB &00 ; Padding to fill to BDOS0+6 DEFW &0000 ; The standard CP/M 2.2 BDOS follows here ORG BIOS0 ; BIOS squeezed into final 512 bytes ; BIOS JUMP TABLE ; =============== BOOT: JP JBOOT WBOOT: JP JWBOOT CONST: JP JCONST CONIN: JP JCONIN CONOUT: JP JCONOUT LST: JP JLST PUNCH: JP JPUNCH READER: JP JREADER HOME: JP JHOME SELDSK: JP JSELDSK SETTRK: JP JSELTRK SETSEC: JP JSETSEC SETDMA: JP JSETDMA RD: JP JREAD WR: JP JWRITE PRSTAT: JP JPRSTAT SECTRN: JP JSECTRN ; INITIAL ZERO-PAGE CONTENTS ; -------------------------- BASE: JP WBOOT ; Jump to BIOS to Warm Boot DEFW &0000 ; IOBYTE, DRIVE JP BDOS0+6 ; BDOS function call entry ; COLD BOOT - Entered on hard reset ; --------------------------------- JBOOT: DI LD HL,&F000 LD DE,&F000 LD BC,&1000 LDIR ; Copy ROM into RAM JWBOOT LD SP,&0100 ; Initial stack under TPA LD HL,BASE LD DE,&0000 LD BC,8 LDIR ; Set up zero page LD A,&13 ; 8-bit data, no parity OUT (CONTROL),A ; Initilise 6850, page out ROM LD C,&0D CALL BDOS ; Reset disk system LD HL,CCPNAME LD DE,FCB LD BC,16 LDIR ; Copy CCP name to FCB LD C,&0F CALL CCPBDOS ; Open 'CCP.SYS' INC A CCPERROR: JR Z,CCPERROR ; Can't find CCP.SYS LD DE,CCP ; Load to CCP space PUSH DE ; Stack entry address CCPLOOP: PUSH DE ; Save current load address LD C,&1A CALL BDOS ; Set DMA address LD A,&14 CALL CCPBDOS ; Read a record AND A JR NZ,CCPEND ; EOF found POP DE LD HL,128 ADD HL,DE EX DE,HL JR CCPLOOP ; Loop to load CCP CCPEND: LD C,&10 ; Fall through to close file ; And pop to enter CCP CCPBDOS LD DE,FCB ; Point to CCP FCB CALL BDOS LD C,&00 ; Initial drive A: RET ; Return to caller or enter CCP CCPNAME: DEFB 0 ; Drive A DEFB "CCP SYS" ; File 'CCP.SYS' DEFB 0,0,0,0 ; CONSOLE I/O VIA A 6850 SERIAL ACIA ; ================================== JCONST: IN A,(STATUS) ; Read 6850 status BIT 0,A ; Check RxReady LD A,&FF ; &FF = RxReady RET NZ XOR A ; &00 = NOT RxReady RET JCONIN: CALL JCONST JR Z,JCONIN ; Wait until char present IN A,(DATA) ; Get character RET JCONOUT: IN A,(STATUS) ; Get 6850 status BIT 1,A ; Check TxRdy JR Z,JCONOUT ; Loop until empty LD A,C OUT (DATA),A ; Send data JLST: JPUNCH: JREADER: JPRSTAT: JSECTRN: RET ; Do nothing ; DISK ACCESS ; =========== JSETDMA: LD (DMA),BC ; Set DMA address RET JHOME: LD BC,0 ; Select track zero JSELTRK: LD (TRACK),BC ; Store track RET JSETSEC: LD (SECTOR),BC ; Store sector RET JSELDSK: LD HL,DPH ; Return same DPH RET IDE_SEL: LD A,1 OUT (IDE_COUNT),A ; One sector LD A,(SECTOR) OUT (IDE_SEC),A ; Set sector LD A,(TRACK+0) OUT (IDE_CYL+0),A ; Cylinder LD A,(TRACK+1) OUT (IDE_CYL+1),A ; Cylinder XOR A OUT (IDE_HEAD),A LD HL,(DMA) LD B,128 RET JREAD: CALL IDE_SEL ; Set IDE parameters LD A,&20 OUT (IDE_CMD),A ; IDE command &20 = read RDLP: IN A,(IDE_DATA) ; Waste bytes for simplicity IN A,(IDE_DATA) LD (HL),A DJNZ RDLP XOR A ; No checking for errors! RET JWRITE: CALL IDE_SEL ; Set IDE parameters LD A,&30 OUT (IDE_CMD),A ; IDE command &30 = write WRLP: LD (HL),A OUT (IDE_DATA),A ; Waste bytes for simplicity OUT (IDE_DATA),A DJNZ WRLP XOR A ; No checking for errors! RET DPH: DEFW &0000 DEFW &0000 DEFW &0000 DEFW &0000 DEFW DIRBUF ; 128-byte buffer DEFW DPB DEFW CSV DEFW ALV DPB: 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 &0000 ; OFF=0 Reserved tracks before logical disk start DMA: DEFW &0000 SECTOR: DEFW &0000 TRACK: DEFW &0000 DIRBUF: DEFS 128 ; 128-byte buffer CSV: DEFS 32 ; 32-byte checksum vector ALV: DEFS 25 ; Allocation vector NOP ; END