diff options
author | H. Peter Anvin <hpa@zytor.com> | 2014-05-09 21:52:52 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2014-05-09 21:52:52 -0700 |
commit | c224dce6bb9f07a65aafc4568b902d78394db8d8 (patch) | |
tree | 90c7dea74c9b12ef70614e84d1b5e6896abcb32a | |
parent | 6ff5c30b43a24615b2e669cefcedf24cb63bec27 (diff) | |
download | abc80-c224dce6bb9f07a65aafc4568b902d78394db8d8.tar.gz abc80-c224dce6bb9f07a65aafc4568b902d78394db8d8.tar.xz abc80-c224dce6bb9f07a65aafc4568b902d78394db8d8.zip |
CP/M: Add CBIOS plus change the loader
Make the loader simply POKE the (C)BIOS into memory and then
copy it into place... a bit easier that way.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | data/Makefile | 4 | ||||
-rw-r--r-- | data/cbios.asm | 554 | ||||
-rw-r--r-- | data/cpm.asm | 60 |
3 files changed, 569 insertions, 49 deletions
diff --git a/data/Makefile b/data/Makefile index c11fe5a..673889c 100644 --- a/data/Makefile +++ b/data/Makefile @@ -56,8 +56,10 @@ chargen.mif : chargen.bin $(BIN2MIF) videoram.bin: video.txt genvideo.pl $(PERL) genvideo.pl -40 < $< > $@ || ( rm -f $@ ; exit 1 ) +cpm.bin: cpm.asm cbios.bin + cpm.bas: cpm.bin - $(PERL) bin2poke.pl $< 32768 1 'Z%=CALL(32768%)' > $@ \ + $(PERL) bin2poke.pl $< 32768 100 'Z%=CALL(32768%)' > $@ \ || ( rm -f $@ ; exit 1 ) .bin.bas: diff --git a/data/cbios.asm b/data/cbios.asm new file mode 100644 index 0000000..f64ccdf --- /dev/null +++ b/data/cbios.asm @@ -0,0 +1,554 @@ +; SOCZ80 CP/M 2.2 BIOS (or at least an attempt!) +; 2013-11-03 Will Sowerbutts + +; useful resources: +; http://www.cpm.z80.de/manuals/cpm22-m.pdf +; http://www.retrotechnology.com/dri/howto_cpm.html#myown +; http://www.retrotechnology.com/dri/cpm_features.html +; cpmtools package + +msize: equ 64 ; KB of system RAM +biosextra: equ 512 +bias: equ (msize-20)*1024-biosextra +ccp: equ 0x3400+bias +bdos: equ ccp+0x0806 +bios: equ ccp+0x1600 +cdisk: equ 0x0004 ; current disk number + ; 0=a, 1=b ... 15=p + ; bits 7:4 is the current USER +iobyte: equ 0x0003 ; Intel "standard" I/O byte +currow: equ 0xFFFE ; Cursor row +curcol: equ 0xFFFF ; Cursor column + + org bios + +; jump vector table used by CP/M + jp boot ; cold start +wboote: 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 out + jp home ; move disk head to home position + jp seldsk ; select disk + jp seltrk ; set track number + jp setsec ; set setor number + jp setdma ; set DMA address + jp read ; read disk + jp write ; write disk + jp listst ; return list status + jp sectran ; sector translate + +; Interrupt vectors (must be aligned to an even byte boundary) + align 2 +kbdvec: defw irq_kbd +v24vec: defw irq_v24 + +; disk parameter header (16 bytes for each drive), see page 6-28 in CP/M 2.2 operating system manual +dpbase: + ; disk 0 (A) + dw 0 ; sector translation table (0 = no translation) + dw 0 ; must be 0 + dw 0 ; must be 0 + dw 0 ; must be 0 + dw dirbf ; DIRBUF (address of shared 128 byte scratch pad area) + dw dpblk ; DPB (disk parameter block) + dw chk00 ; CSV (unique scratch pad used to check for changed disks) + dw alv00 ; ALV (unique scratch pad for allocation information) + ; end of disk 0 + + ; disk 1 (B) + dw 0 ; sector translation table (0 = no translation) + dw 0 ; must be 0 + dw 0 ; must be 0 + dw 0 ; must be 0 + dw dirbf ; DIRBUF (address of shared 128 byte scratch pad area) + dw dpblk ; DPB (disk parameter block) + dw chk01 ; CSV (unique scratch pad used to check for changed disks) + dw alv01 ; ALV (unique scratch pad for allocation information) + ; end of disk 1 + + ; disk 2 (C) + dw 0 ; sector translation table (0 = no translation) + dw 0 ; must be 0 + dw 0 ; must be 0 + dw 0 ; must be 0 + dw dirbf ; DIRBUF (address of shared 128 byte scratch pad area) + dw dpblk ; DPB (disk parameter block) + dw chk02 ; CSV (unique scratch pad used to check for changed disks) + dw alv02 ; ALV (unique scratch pad for allocation information) + ; end of disk 2 + + ; disk 3 (D) + dw 0 ; sector translation table (0 = no translation) + dw 0 ; must be 0 + dw 0 ; must be 0 + dw 0 ; must be 0 + dw dirbf ; DIRBUF (address of shared 128 byte scratch pad area) + dw dpblk ; DPB (disk parameter block) + dw chk03 ; CSV (unique scratch pad used to check for changed disks) + dw alv03 ; ALV (unique scratch pad for allocation information) + ; end of disk 3 + +ndisks: equ 4 ; number of disks we defined + +; disk parameter block (can be shared by all disks with same configuration) +; each volue is 15,597,568 bytes (238*256*256). We represent this as +; tracks of 8192 bytes (64 sectors x 128 bytes) which matches an UFD-DOS +; "cluster". This means there are 1904 tracks. The first track is reserved +; for the system, leaving 1903*64 = 121792 user sectors. +; We use 8K blocks, matching ABC clusters, so there are 1903 user blocks. +dpblk: + dw 64 ; SPT: number of 128 byte sectors per track + db 5 ; BSH: block shift factor (see manual for table) + db 31 ; BLM: block mask (see manual for table) + db 1 ; EXM: extent mask (see manual for table, using entries marked N/A turns out to be a bad idea!) + dw 1903 ; DSM: (disk bytes / block bytes) - 1, change alv0x etc if you change this; this is the number of the last sector on the disk, excluding system tracks (ie more system tracks -> this gets smaller) + dw 511 ; DRM: directory max entries - 1 + db 0xf0 ; AL0: directory sector allocation bitmask byte 0 + db 0x00 ; AL1: directory sector allocation bitmask byte 1 + dw 0 ; CKS: check size (change chk0x etc if you change this) + dw 1 ; OFF: track offset (number of system tracks) + + align 2 +; Screen row address table +rowtbl: + defw 7400h + defw 7500h + defw 7600h + defw 7700h + defw 7C00h + defw 7D00h + defw 7E00h + defw 7F00h + defw 7450h + defw 7550h + defw 7650h + defw 7750h + defw 7C50h + defw 7D50h + defw 7E50h + defw 7F50h + defw 74A0h + defw 75A0h + defw 76A0h + defw 77A0h + defw 7CA0h + defw 7DA0h + defw 7EA0h + defw 7FA0h + +pio_init: + ; Port A initialization sequence + defb 0CFh, 0FFh, kbdvec & 255, 0B7h, 080h + ; Port B initialization sequence + defb 0CFh, 087h, v24vec & 255, 0B7h, 000h + +; bios functions follow +boot: ; Cold boot + di + ld a,7 + out (7),a ; Map 3, NMI disabled + ld sp,0x100 ; Put our stack in the default DMA buffer + + xor a + ld (iobyte), a + ; say hello + ld hl,bootmsg +do_boot_msg: + xor a + ld c,(hl) + inc hl + cp c + jr z,msg_done + push hl + call conout + pop hl + jr do_boot_msg +msg_done: + ; fall through to wboot + +wboot: ; warm boot -- reload CCP and then run it again, + ; we also reload BDOS for good measure + ; CCP+BDOS is located in track 0 at sector offset 0 + ; CCP+BDOS is 0x1600 bytes long, ie 44 sectors. + ; so we read 44 sectors from sector 20 onwards + ; into memory starting at address ccp + + ; put our stack in the default DMA buffer + di + ld a,7 + out (7),a ; Map 3, NMI disabled + ld sp,0x100 ; Put our stack in the default DMA buffer + + ; Configure interrupts + im 2 + ld a,kbdvec/256 + ld i,a + + ; Configure PIO port A (keyboard) + ld bc,57 + 5*256 + ld hl,pio_init + otir + + ; Configure PIO port B (cassette/V.24) + ld bc,59 + 5*256 + otir + + ; Enable interrupts + ei + + ; select disk 0 + ld c, 0 + call seldsk + + ; select track 0 + call home + + ; point DMA target at ccp base + ld bc, ccp + call setdma + + ; select first sector + ld c, 0 + call setsec + + ; count of sectors to load + ld de, 44 + push de ; store remaining sector count + +wbootloop: + ; read sector + call read + pop de ; recover remaining sector count + dec de + ld a, d + or e + jr z, gocpm ; boot CP/M after loading the final sector + push de ; stash sector count + + ; advance to next sector + ld a, (cursector) + inc a + ld (cursector), a + + ; advance to next DMA address + ld hl, (curdmaaddr) + ld bc, 0x80 + add hl, bc + ld (curdmaaddr), hl + + ; loop and read in the next sector + jr wbootloop + +gocpm: + ld a, 0xc3 ; 0xc3 is a jmp instruction + ld (0), a ; write JMP instruction at 0x0000 + ld (5), a ; write JMP instruction at 0x0005 + ld hl, wboote + ld (1), hl ; write vector for warm boot at 0x0001, 0x0002. + ld hl, bdos ; BDOS entry point + ld (6), hl ; write vector for BDOS at 0x0006, 0x0007. + ld bc, 0x0080 ; default DMA address + call setdma ; configure DMA + ld a, (cdisk) ; get current disk + ld c, a ; send to ccp + jp ccp ; and we're off! + +irq_kbd: +irq_v24: + reti + +const: ; read console status +connotready: + xor a + ret + +conin: ; read character from console into A; wait if no character ready + xor a + ret + +conout: ; write chracter from C to console + ld a,c + sub 08h + jp z,con_bs + dec a + jp z,con_tab + dec a + jr z,con_lf + dec a + dec a + jp z,con_ff + dec a + jr z,con_cr + ; Fall through +con_print: + ld a,(currow) + add a + add rowtbl & 0xff + ld l,a + ld h,rowtbl/256 + ld a,(curcol) + ld b,a + add (hl) + ld e,a + inc l + ld d,(hl) + di + ld a,4 ; Map 0, NMI off + out (7),a + ld a,c + ld (de),a + ld a,7 ; Map 3, NMI off + out (7),a + ei + ld a,b + inc a + ld (curcol),a + sub 80 + ret nz + ld (curcol),a + ; Fall through +con_lf: + ld a,(currow) + inc a + ld (currow),a + cp 24 + ret nz + ; Need to scroll... + dec a + ld (currow),a + push ix + di + ld a,4 + out (7),a ; Map 0, NMI off + ld a,23 + ld ix,rowtbl + ld b,0 +con_lfloop: + ld e,(ix+0) + ld d,(ix+1) + ld l,(ix+2) + ld h,(ix+3) + ld c,80 + ldir + inc ix + inc ix + dec a + jr nz,con_lfloop + ; Clear the row which IX currently points to + ld l,(ix+0) + ld h,(ix+1) + ld e,l + ld d,h + inc e + ld c,32 ; space + ld (hl),c + ld bc,79 + ldir +con_pop_done: + ld a,7 ; Map 3, NMI off + out (7),a + ei + pop ix + ret +con_cr: + xor a + ld (curcol),a + ret +con_bs: + ld a,(curcol) + dec a + ret c + ld (curcol),a + ret +con_tab: + ld c,32 + call con_print + ld a,(curcol) + and 07h + jr nz,con_tab + ret +con_ff: + push ix + di + ld a,4 + out (7),a ; Map 0, NMI off + xor a + ld (curcol),a + ld (currow),a + ld a,24 + ld ix,rowtbl +con_ffloop: + ; Clear the row which IX currently points to + ld l,(ix+0) + ld h,(ix+1) + ld e,l + ld d,h + inc e + ld c,32 ; space + ld (hl),c + ld bc,79 + ldir + inc ix + inc ix + dec a + jr nz,con_ffloop + jr con_pop_done + +list: ; write character to listing device (we don't have one!) + ret + +listst: ; return list device status + xor a ; 0 = not ready + ret + +punch: ; write character to punch device (we don't have one!) + ret + +reader: ; read character from reader device (which we don't have) + ld a, 0x1a ; end of file + ret + +seldsk: ; select disk indicated by register C + ld hl, 0 ; return code 0 indicates error + ld a, c + cp ndisks + ret nc ; return (with error code) if C >= ndisks ie illegal drive + ld (curdisk), a ; store current disk + ; compute proper disk parameter header address in HL + add a,a ; *2 + add a,a ; *4 + add a,a ; *8 + add a,a ; *16 + add a,l + ld l,a + if (dpbase & 0xff) > (256-ndisks*16) + ld a,h + adc a,0 + ld h,a + endif + ; HL = dpbase + disk number * 16 + ret + +home: ld bc, 0 + ; fall through into seltrk +seltrk: ; set track given by register BC + ld (curtrack), bc + ret + +setsec: ; set sector given by register (B)C + ld a,c + ld (cursector),a + ret + +sectran: ; logical to physical sector translation + ; HL=BC ie 1:1 mapping (no translation) + ld h, b + ld l, c + ret + +setdma: ; set DMA address given by BC + ld (curdmaaddr),bc + ret + +read: ; read a sector from disk + ; XXX: add error handing! + ld c,03h ; READ + SECTOR TO HOST + call sendcmd + ld hl,(curdmaaddr) + ld bc,128 + inir + xor a ; Success! + ret + +write: ; write sector to disk + ; XXX: add error handing! + ld c,0Ch ; SECTOR FROM HOST + WRITE + call sendcmd + ld hl,(curdmaaddr) + ld bc,128 + otir + xor a ; Success! + ret + + ; Send a controller command, C = K0 command byte + ; Clobbers A DE HL, returns with disk selected +sendcmd: + ld a,36 + out (1),a ; Select disk + out (2),a ; Start command + + ld a,(curdisk) ; curdisk + or 24h ; +4 for volume, CP/M mode + ld e,a + ld a,(cursector) + rrca + jr nc,bc1 + set 4,e ; odd CP/M sector +bc1: + ld d,0 + ld hl,(curtrack) + srl h + rr l + rr d + srl h + rr l + rr d + srl h + rr l + rr d + or d + ld h,a + + ; Now: K0 = C, K1 = E, K2 = L, K3 = H +sc_wait1: + in a,(1) ; Status port + rlca + jr nc,sc_wait1 + + ld a,c + call sc_out_wait_ok + ld a,e + call sc_out_wait_ok + ld a,l + call sc_out_wait_ok + ld a,h + ; Fall through +sc_out_wait_ok: + out (0),a +sc_wait_ok: + in a,(1) + rrca + ret c + jr sc_wait_ok + +bootmsg: db "CP/M BIOS (H. Peter Anvin, 2014-05-09)\r\n" + db "CP/M 2.2 Copyright 1979 (c) by Digital Research\r\n" + db 0 + +;--------------------------------------------------------------------------------------------------------------- + + + +; scratch RAM used by BIOS +curdisk: db 0 +cursector: db 0 +curtrack: dw 0 +curdmaaddr: dw 0 + +; scratch RAM used by BDOS +dirbf: ds 128 ; directory scratch area +alv00: ds 238 ; allocation vector for disk 0, must be (DSM/8)+1 bytes +alv01: ds 238 ; allocation vector for disk 1, must be (DSM/8)+1 bytes +alv02: ds 238 ; allocation vector for disk 2, must be (DSM/8)+1 bytes +alv03: ds 238 ; allocation vector for disk 3, must be (DSM/8)+1 bytes +chk00: ds 0 ; check vector for disk 0 (must be CKS bytes long) +chk01: ds 0 ; check vector for disk 1 (must be CKS bytes long) +chk02: ds 0 ; check vector for disk 2 (must be CKS bytes long) +chk03: ds 0 ; check vector for disk 3 (must be CKS bytes long) + +db "</bios>" +; we point the stack at 0x0000 on restart, so that may overwrite the very end of RAM. diff --git a/data/cpm.asm b/data/cpm.asm index 1830775..cbbb876 100644 --- a/data/cpm.asm +++ b/data/cpm.asm @@ -15,10 +15,10 @@ ; HDSEL: equ 36 RAMSIZE: equ 64 -BIAS: equ (64-20)*1024 -CPMADDR: equ BIAS+3400h +BIAS: equ (64-20)*1024-512 CPMBIOS: equ BIAS+4A00h -CPMSECTS: equ 50/2 ; Total number of ABC sectors (@256b) +ABCCURSOR: equ 65011 +CPMCURSOR: equ 0FFFEh org 08000h _start: @@ -26,51 +26,15 @@ _start: ld a,7 ; Map 3, NMI disabled out (7),a - ld a,HDSEL - out (1),a + in a,(7) + + ld hl,bios + ld de,CPMBIOS + ld bc,bios_size + ldir - ld hl,CPMADDR - ld sp,hl - xor a - ld e,a ; Current sector - -get_sect: - out (2),a ; Start command - -wait_ready: - in a,(1) ; Status port - rlca - jr nc,wait_ready - - ld a,3 ; READ SECTOR + SECTOR TO HOST - call out_wait_ok - - ld a,4 ; Unit 4, buffer 0, 256 byte sector - call out_wait_ok - - xor a ; Track 0 - call out_wait_ok - - ld a,e ; Sector number - inc e - call out_wait_ok - - xor a - ld b,a ; 256 bytes - ld c,a ; Port 0 - inir - - ld a,CPMSECTS - cp e - jr nz,get_sect - - in a,(7) ; Bus reset jp CPMBIOS -out_wait_ok: - out (0),a -wait_ok: - in a,(1) - rrca - ret c - jr wait_ok +bios: + incbin "cbios.bin" +bios_size: equ $-bios |