--- /dev/null
+; Z80 CBIOS
+.area BIOS (ABS)
+
+; configuration
+MSIZE = 20 ; memory size
+NDRIVES = 2 ; number of drives
+TTY_D = 0x10 ; usart data port
+TTY_S = 0x11 ; usart status port
+ROMDISK = 0x0f ; romdisk port
+
+; constants
+BIAS = (MSIZE-20)*1024 ; address offset from 0x3400
+CCP = (0x3400+BIAS) ; ccp base
+BDOS = (CCP+0x806) ; bdos base
+BIOS = (CCP+0x1600) ; bios base
+NSECTS = (BIOS-CCP)/128 ; sector count for CCP+BDOS
+
+IOBYTE = 3 ; address of IOBYTE
+CDISK = 4 ; address of CDISK
+
+;============================================================================
+; COLD BOOT ROUTINE
+.org 0
+ LD SP, #0x80
+ JP BIOS
+
+;============================================================================
+;BIOS ENTRY POINT
+.org BIOS
+ JP BOOT ; COLD START
+ JP WBOOT ; WARM START
+ JP CONST ; CONSOLE STATUS
+ JP CONIN ; CONSOLE CHARACTER IN
+ JP CONOUT ; CONSOLE CHARACTER OUT
+ JP LIST ; LIST CHARACTER OUT
+ JP PUNCH ; PUNCH CHARACTER OUT
+ JP READER ; READER CHARACTER IN
+ JP HOME ; MOVE HEAD TO HOME
+ JP SELDSK ; SELECT DISK
+ JP SETTRK ; SET TRACK NUMBER
+ JP SETSEC ; SET SECTOR NUMBER
+ JP SETDMA ; SET DMA ADDRESS
+ JP READ ; READ DISK
+ JP WRITE ; WRITE DISK
+ JP LISTST ; RETURN LIST STATUS
+ JP SECTRAN ; SECTOR TRANSLATE
+
+;============================================================================
+; DATA TABLES
+DPHEAD:
+ ; A: ROM DISK
+ .dw 0x0000, 0x0000, 0x0000, 0x0000
+ .dw DIRBF, DPB_A, 0x0000, ALV_A
+
+ ; B: 8MB DRIVE
+ .dw 0x0000, 0x0000, 0x0000, 0x0000
+ .dw DIRBF, DPB_B, CHK_B, ALV_B
+
+DPB_A:
+ .dw 1 ; SPT -> 1 sector/track
+ .db 3, 7 ; BSH, BLM -> 1k blocksize
+ .db 0 ; EXM -> no extends
+ .dw 23 ; DSM -> 24 blocks
+ .dw 31 ; DRM -> 32 directory entries
+ .db 0x80,0x00 ; AL0/AL1 -> 1 block used for directory
+ .dw 0 ; CKS -> fixed disk (no change scratchpad)
+ .dw 44 ; OFF -> 44 reserved tracks (5.5 KB)
+
+DPB_B:
+ ; FIXME
+ .db 26,0,3,7,0,242,0,63,0,192,0,16,0,2,0
+
+;============================================================================
+; MESSAGES
+MSG_SIGNON: .asciz "CP/M 2.2 CBIOS v0\r\n"
+
+;============================================================================
+OUTMSG:
+ LD A,(HL) ; load char
+ OR A,A
+ RET Z ; return if zero
+ OUT (TTY_D),A
+ INC HL ; next char
+ JR OUTMSG
+
+OUTHEX: ; input = HL
+ LD C, H
+ CALL OUTHEX8
+ LD C, L
+OUTHEX8: ; input = C
+ LD A, C
+ RRA
+ RRA
+ RRA
+ RRA
+ CALL OUTHEXC
+ LD A, C
+OUTHEXC:
+ AND #0x0F
+ ADD A, #0x90
+ DAA
+ ADC A, #0x40
+ DAA
+ OUT (TTY_D), A
+ RET
+
+;============================================================================
+BOOT:
+ LD HL, #MSG_SIGNON
+ CALL OUTMSG
+
+ XOR A
+ LD (IOBYTE),A ; CON:TTY,RDR:TTY,PUN:TTY,LST:TTY
+ LD (CDISK),A ; drive A
+ ;JP GOCPM
+
+;============================================================================
+WBOOT:
+ ; read cp/m
+ LD HL, #CCP ; destination
+ LD C, #ROMDISK ; port
+ LD D, #(NSECTS) ; count sectors down
+ LD A, #0 ; count sectors up
+1$:
+ OUT (C),A ; select track
+ LD B, #128 ; number of bytes
+ INIR ; read
+ INC A ; next track
+ DEC D ; one sector less
+ JR NZ, 1$ ; repeat
+
+ JP GOCPM
+
+;============================================================================
+GOCPM:
+ LD A, #0xC3 ; JP opcode
+ LD (0), A ; ...at 0x0000
+ LD (5), A ; ...at 0x0005
+ LD HL, #WBOOT
+ LD (1), HL ; WBOOT pointer at 0x0001
+ LD HL, #BDOS
+ LD (6), HL ; BDOS pointer at 0x0006
+
+ LD BC, #0x0080
+ CALL SETDMA ; default DMA address 0x0080
+
+ EI ; enable interrupts
+ LD A, (CDISK)
+ LD C, A ; set active drive
+
+ JP CCP ; go to cp/m
+
+;============================================================================
+CONST:
+ IN A, (#TTY_S)
+ OR A,A
+ RET Z ; no data: A=0x00
+ LD A, #0xFF
+ RET ; else: A=0xFF
+
+;============================================================================
+CONIN:
+ CALL CONST
+ JR Z, CONIN
+ IN A, (TTY_D) ; read data
+ AND #0x7F ; strip parity bit
+ RET
+
+;============================================================================
+CONOUT:
+ LD A, C
+ OUT (TTY_D), A ; write data
+ RET
+
+;============================================================================
+LIST:
+ RET
+
+;============================================================================
+PUNCH:
+ RET
+
+;============================================================================
+READER:
+ LD A, #0x1A
+ AND #0x7F
+ RET
+
+;============================================================================
+HOME:
+ LD C, #0
+ JP SETTRK ; will return for us
+
+;============================================================================
+; select disk from C (0..15 => A..P)
+; return HL=dph or 0x0000 on error
+SELDSK:
+ ; check for valid drive
+ LD HL, #0 ; error code
+ LD A, C
+ LD (DISKNO), A ; select drive
+ CP #NDRIVES
+ RET NC ; return if >NDRIVES
+
+ ; put disk parameter header in HL
+ LD H, #0
+ LD L, A ; drive number in HL
+ ADD HL, HL
+ ADD HL, HL
+ ADD HL, HL
+ ADD HL, HL ; * sizeof(dph)
+ LD DE, #DPHEAD
+ ADD HL, DE ; HL = table base + 16*drive
+ RET
+
+;============================================================================
+SETTRK:
+ LD (TRACK), BC
+ RET
+
+;============================================================================
+SETSEC:
+ LD (SECTOR), BC
+ RET
+
+;============================================================================
+SETDMA:
+ LD (DMAAD), BC
+ RET
+
+;============================================================================
+; return A=0 on success, A=1 on error; do retry
+READ:
+ LD A,(TRACK)
+ OUT (ROMDISK),A ; select sector=track
+
+ LD HL,(DMAAD) ; select destination
+ LD C, #ROMDISK ; disk data port
+ LD B, #128 ; 128 bytes
+ INIR ; read
+
+ LD A, #0
+ RET
+
+;============================================================================
+; return A=0 on success, A=1 on error; do retry
+WRITE:
+ LD A, #1
+ RET ; fail
+
+;============================================================================
+; return A=0 if printer busy, A=0xFF otherwise
+LISTST:
+ XOR A
+ RET ; fail
+
+;============================================================================
+; logical sector number in BC and translation table address in DE,
+; return physical sector in HL
+SECTRAN:
+ RET
+
+
+;============================================================================
+; VARIABLES AND SCRATCH RAM
+DISKNO: .ds 1 ; selected disk
+TRACK: .ds 2 ; selected track
+SECTOR: .ds 2 ; selected sector
+DMAAD: .ds 2 ; selected DMA address
+
+DIRBF: .ds 128 ; shared directory buffer
+ALV_A: .ds 3 ; allocation buffer for ROM DISK
+CHK_B: .ds 16 ; FIXME
+ALV_B: .ds 31 ; FIXME
+