# File z80_cbios.asm 0000 ; skeletal cbios for first level of CP/M 2.0 alteration 0000 ; 0000 ccp: equ 0E400h ;base of ccp 0000 bdos: equ 0EC06h ;bdos entry 0000 bios: equ 0FA00h ;base of bios 0000 cdisk: equ 0004h ;address of current disk number 0=a,... l5=p 0000 iobyte: equ 0003h ;intel i/o byte 0000 disks: equ 04h ;number of disks in the system 0000 ; 0000 org bios ;origin of this program fa00 nsects: equ ($-ccp)/128 ;warm start sector count fa00 ; fa00 ; jump vector for individual subroutines fa00 ; fa00 c3 9c fa JP boot ;cold start fa03 c3 a6 fa wboote: JP wboot ;warm start fa06 c3 18 fb JP const ;console status fa09 c3 25 fb JP conin ;console character in fa0c c3 31 fb JP conout ;console character out fa0f c3 3c fb JP list ;list character out fa12 c3 40 fb JP punch ;punch character out fa15 c3 42 fb JP reader ;reader character out fa18 c3 47 fb JP home ;move head to home position fa1b c3 4d fb JP seldsk ;select disk fa1e c3 66 fb JP settrk ;set track number fa21 c3 6b fb JP setsec ;set sector number fa24 c3 77 fb JP setdma ;set dma address fa27 c3 7d fb JP read ;read disk fa2a c3 d7 fb JP write ;write disk fa2d c3 3e fb JP listst ;return list status fa30 c3 70 fb JP sectran ;sector translate fa33 ; fa33 ; fixed data tables for four-drive standard fa33 ; ibm-compatible 8" disks fa33 ; no translations fa33 ; fa33 ; disk Parameter header for disk 00 fa33 00 00 00 00 dpbase: defw 0000h, 0000h fa37 00 00 00 00 defw 0000h, 0000h fa3b 36 fc 8d fa defw dirbf, dpblk fa3f 32 fd b6 fc defw chk00, all00 fa43 ; disk parameter header for disk 01 fa43 00 00 00 00 defw 0000h, 0000h fa47 00 00 00 00 defw 0000h, 0000h fa4b 36 fc 8d fa defw dirbf, dpblk fa4f 42 fd d5 fc defw chk01, all01 fa53 ; disk parameter header for disk 02 fa53 00 00 00 00 defw 0000h, 0000h fa57 00 00 00 00 defw 0000h, 0000h fa5b 36 fc 8d fa defw dirbf, dpblk fa5f 52 fd f4 fc defw chk02, all02 fa63 ; disk parameter header for disk 03 fa63 00 00 00 00 defw 0000h, 0000h fa67 00 00 00 00 defw 0000h, 0000h fa6b 36 fc 8d fa defw dirbf, dpblk fa6f 62 fd 13 fd defw chk03, all03 fa73 ; fa73 ; sector translate vector fa73 01 07 0d 13 trans: defm 1, 7, 13, 19 ;sectors 1, 2, 3, 4 fa77 19 05 0b 11 defm 25, 5, 11, 17 ;sectors 5, 6, 7, 6 fa7b 17 03 09 0f defm 23, 3, 9, 15 ;sectors 9, 10, 11, 12 fa7f 15 02 08 0e defm 21, 2, 8, 14 ;sectors 13, 14, 15, 16 fa83 14 1a 06 0c defm 20, 26, 6, 12 ;sectors 17, 18, 19, 20 fa87 12 18 04 0a defm 18, 24, 4, 10 ;sectors 21, 22, 23, 24 fa8b 10 16 defm 16, 22 ;sectors 25, 26 fa8d ; fa8d dpblk: ;disk parameter block for all disks. fa8d 1a 00 defw 26 ;sectors per track fa8f 03 defm 3 ;block shift factor fa90 07 defm 7 ;block mask fa91 00 defm 0 ;null mask fa92 f2 00 defw 242 ;disk size-1 fa94 3f 00 defw 63 ;directory max fa96 c0 defm 192 ;alloc 0 fa97 00 defm 0 ;alloc 1 fa98 00 00 defw 0 ;check size fa9a 02 00 defw 2 ;track offset fa9c ; fa9c ; end of fixed tables fa9c ; fa9c ; individual subroutines to perform each function fa9c boot: ;simplest case is to just perform parameter initialization fa9c af XOR a ;zero in the accum fa9d 32 03 00 LD (iobyte),A ;clear the iobyte faa0 32 04 00 LD (cdisk),A ;select disk zero faa3 c3 ef fa JP gocpm ;initialize and go to cp/m faa6 ; faa6 wboot: ;simplest case is to read the disk until all sectors loaded faa6 31 80 00 LD sp, 80h ;use space below buffer for stack faa9 0e 00 LD c, 0 ;select disk 0 faab cd 4d fb call seldsk faae cd 47 fb call home ;go to track 00 fab1 ; fab1 06 2c LD b, nsects ;b counts * of sectors to load fab3 0e 00 LD c, 0 ;c has the current track number fab5 16 02 LD d, 2 ;d has the next sector to read fab7 ; note that we begin by reading track 0, sector 2 since sector 1 fab7 ; contains the cold start loader, which is skipped in a warm start fab7 21 00 e4 LD HL, ccp ;base of cp/m (initial load point) faba load1: ;load one more sector faba c5 PUSH BC ;save sector count, current track fabb d5 PUSH DE ;save next sector to read fabc e5 PUSH HL ;save dma address fabd 4a LD c, d ;get sector address to register C fabe cd 6b fb call setsec ;set sector address from register C fac1 c1 pop BC ;recall dma address to b, C fac2 c5 PUSH BC ;replace on stack for later recall fac3 cd 77 fb call setdma ;set dma address from b, C fac6 ; fac6 ; drive set to 0, track set, sector set, dma address set fac6 cd 7d fb call read fac9 fe 00 CP 00h ;any errors? facb c2 a6 fa JP NZ,wboot ;retry the entire boot if an error occurs face ; face ; no error, move to next sector face e1 pop HL ;recall dma address facf 11 80 00 LD DE, 128 ;dma=dma+128 fad2 19 ADD HL,DE ;new dma address is in h, l fad3 d1 pop DE ;recall sector address fad4 c1 pop BC ;recall number of sectors remaining, and current trk fad5 05 DEC b ;sectors=sectors-1 fad6 ca ef fa JP Z,gocpm ;transfer to cp/m if all have been loaded fad9 ; fad9 ; more sectors remain to load, check for track change fad9 14 INC d fada 7a LD a,d ;sector=27?, if so, change tracks fadb fe 1b CP 27 fadd da ba fa JP C,load1 ;carry generated if sector<27 fae0 ; fae0 ; end of current track, go to next track fae0 16 01 LD d, 1 ;begin with first sector of next track fae2 0c INC c ;track=track+1 fae3 ; fae3 ; save register state, and change tracks fae3 c5 PUSH BC fae4 d5 PUSH DE fae5 e5 PUSH HL fae6 cd 66 fb call settrk ;track address set from register c fae9 e1 pop HL faea d1 pop DE faeb c1 pop BC faec c3 ba fa JP load1 ;for another sector faef ; faef ; end of load operation, set parameters and go to cp/m faef gocpm: faef 3e c3 LD a, 0c3h ;c3 is a jmp instruction faf1 32 00 00 LD (0),A ;for jmp to wboot faf4 21 03 fa LD HL, wboote ;wboot entry point faf7 22 01 00 LD (1),HL ;set address field for jmp at 0 fafa ; fafa 32 05 00 LD (5),A ;for jmp to bdos fafd 21 06 ec LD HL, bdos ;bdos entry point fb00 22 06 00 LD (6),HL ;address field of Jump at 5 to bdos fb03 ; fb03 01 80 00 LD BC, 80h ;default dma address is 80h fb06 cd 77 fb call setdma fb09 ; fb09 fb ei ;enable the interrupt system fb0a 3a 04 00 LD A,(cdisk) ;get current disk number fb0d fe 04 cp disks ;see if valid disk number fb0f da 14 fb jp c,diskok ;disk valid, go to ccp fb12 3e 00 ld a,0 ;invalid disk, change to disk 0 fb14 4f diskok: LD c, a ;send to the ccp fb15 c3 00 e4 JP ccp ;go to cp/m for further processing fb18 ; fb18 ; fb18 ; simple i/o handlers (must be filled in by user) fb18 ; in each case, the entry point is provided, with space reserved fb18 ; to insert your own code fb18 ; fb18 const: ;console status, return 0ffh if character ready, 00h if not fb18 db 03 in a,(3) ;get status fb1a e6 02 and 002h ;check RxRDY bit fb1c ca 22 fb jp z,no_char fb1f 3e ff ld a,0ffh ;char ready fb21 c9 ret fb22 3e 00 no_char:ld a,00h ;no char fb24 c9 ret fb25 ; fb25 conin: ;console character into register a fb25 db 03 in a,(3) ;get status fb27 e6 02 and 002h ;check RxRDY bit fb29 ca 25 fb jp z,conin ;loop until char ready fb2c db 02 in a,(2) ;get char fb2e e6 7f AND 7fh ;strip parity bit fb30 c9 ret fb31 ; fb31 conout: ;console character output from register c fb31 db 03 in a,(3) fb33 e6 01 and 001h ;check TxRDY bit fb35 ca 31 fb jp z,conout ;loop until port ready fb38 79 ld a,c ;get the char fb39 d3 02 out (2),a ;out to port fb3b c9 ret fb3c ; fb3c list: ;list character from register c fb3c 79 LD a, c ;character to register a fb3d c9 ret ;null subroutine fb3e ; fb3e listst: ;return list status (0 if not ready, 1 if ready) fb3e af XOR a ;0 is always ok to return fb3f c9 ret fb40 ; fb40 punch: ;punch character from register C fb40 79 LD a, c ;character to register a fb41 c9 ret ;null subroutine fb42 ; fb42 ; fb42 reader: ;reader character into register a from reader device fb42 3e 1a LD a, 1ah ;enter end of file for now (replace later) fb44 e6 7f AND 7fh ;remember to strip parity bit fb46 c9 ret fb47 ; fb47 ; fb47 ; i/o drivers for the disk follow fb47 ; for now, we will simply store the parameters away for use fb47 ; in the read and write subroutines fb47 ; fb47 home: ;move to the track 00 position of current drive fb47 ; translate this call into a settrk call with Parameter 00 fb47 0e 00 LD c, 0 ;select track 0 fb49 cd 66 fb call settrk fb4c c9 ret ;we will move to 00 on first read/write fb4d ; fb4d seldsk: ;select disk given by register c fb4d 21 00 00 LD HL, 0000h ;error return code fb50 79 LD a, c fb51 32 35 fc LD (diskno),A fb54 fe 04 CP disks ;must be between 0 and 3 fb56 d0 RET NC ;no carry if 4, 5,... fb57 ; disk number is in the proper range fb57 ; defs 10 ;space for disk select fb57 ; compute proper disk Parameter header address fb57 3a 35 fc LD A,(diskno) fb5a 6f LD l, a ;l=disk number 0, 1, 2, 3 fb5b 26 00 LD h, 0 ;high order zero fb5d 29 ADD HL,HL ;*2 fb5e 29 ADD HL,HL ;*4 fb5f 29 ADD HL,HL ;*8 fb60 29 ADD HL,HL ;*16 (size of each header) fb61 11 33 fa LD DE, dpbase fb64 19 ADD HL,DE ;hl=,dpbase (diskno*16) Note typo here in original source. fb65 c9 ret fb66 ; fb66 settrk: ;set track given by register c fb66 79 LD a, c fb67 32 2f fc LD (track),A fb6a c9 ret fb6b ; fb6b setsec: ;set sector given by register c fb6b 79 LD a, c fb6c 32 31 fc LD (sector),A fb6f c9 ret fb70 ; fb70 ; fb70 sectran: fb70 ;translate the sector given by bc using the fb70 ;translate table given by de fb70 eb EX DE,HL ;hl=.trans fb71 09 ADD HL,BC ;hl=.trans (sector) fb72 c9 ret ;debug no translation fb73 6e LD l, (hl) ;l=trans (sector) fb74 26 00 LD h, 0 ;hl=trans (sector) fb76 c9 ret ;with value in hl fb77 ; fb77 setdma: ;set dma address given by registers b and c fb77 69 LD l, c ;low order address fb78 60 LD h, b ;high order address fb79 22 33 fc LD (dmaad),HL ;save the address fb7c c9 ret fb7d ; fb7d read: fb7d ;Read one CP/M sector from disk. fb7d ;Return a 00h in register a if the operation completes properly, and 0lh if an error occurs during the read. fb7d ;Disk number in 'diskno' fb7d ;Track number in 'track' fb7d ;Sector number in 'sector' fb7d ;Dma address in 'dmaad' (0-65535) fb7d ; fb7d 21 72 fd ld hl,hstbuf ;buffer to place disk sector (256 bytes) fb80 db 0f rd_status_loop_1: in a,(0fh) ;check status fb82 e6 80 and 80h ;check BSY bit fb84 c2 80 fb jp nz,rd_status_loop_1 ;loop until not busy fb87 db 0f rd_status_loop_2: in a,(0fh) ;check status fb89 e6 40 and 40h ;check DRDY bit fb8b ca 87 fb jp z,rd_status_loop_2 ;loop until ready fb8e 3e 01 ld a,01h ;number of sectors = 1 fb90 d3 0a out (0ah),a ;sector count register fb92 3a 31 fc ld a,(sector) ;sector fb95 d3 0b out (0bh),a ;lba bits 0 - 7 fb97 3a 2f fc ld a,(track) ;track fb9a d3 0c out (0ch),a ;lba bits 8 - 15 fb9c 3a 35 fc ld a,(diskno) ;disk (only bits fb9f d3 0d out (0dh),a ;lba bits 16 - 23 fba1 3e e0 ld a,11100000b ;LBA mode, select host drive 0 fba3 d3 0e out (0eh),a ;drive/head register fba5 3e 20 ld a,20h ;Read sector command fba7 d3 0f out (0fh),a fba9 db 0f rd_wait_for_DRQ_set: in a,(0fh) ;read status fbab e6 08 and 08h ;DRQ bit fbad ca a9 fb jp z,rd_wait_for_DRQ_set ;loop until bit set fbb0 db 0f rd_wait_for_BSY_clear: in a,(0fh) fbb2 e6 80 and 80h fbb4 c2 b0 fb jp nz,rd_wait_for_BSY_clear fbb7 db 0f in a,(0fh) ;clear INTRQ fbb9 db 08 read_loop: in a,(08h) ;get data fbbb 77 ld (hl),a fbbc 23 inc hl fbbd db 0f in a,(0fh) ;check status fbbf e6 08 and 08h ;DRQ bit fbc1 c2 b9 fb jp nz,read_loop ;loop until clear fbc4 2a 33 fc ld hl,(dmaad) ;memory location to place data read from disk fbc7 11 72 fd ld de,hstbuf ;host buffer fbca 06 80 ld b,128 ;size of CP/M sector fbcc 1a rd_sector_loop: ld a,(de) ;get byte from host buffer fbcd 77 ld (hl),a ;put in memory fbce 23 inc hl fbcf 13 inc de fbd0 10 fa djnz rd_sector_loop ;put 128 bytes into memory fbd2 db 0f in a,(0fh) ;get status fbd4 e6 01 and 01h ;error bit fbd6 c9 ret fbd7 fbd7 write: fbd7 ;Write one CP/M sector to disk. fbd7 ;Return a 00h in register a if the operation completes properly, and 0lh if an error occurs during the read or write fbd7 ;Disk number in 'diskno' fbd7 ;Track number in 'track' fbd7 ;Sector number in 'sector' fbd7 ;Dma address in 'dmaad' (0-65535) fbd7 2a 33 fc ld hl,(dmaad) ;memory location of data to write fbda 11 72 fd ld de,hstbuf ;host buffer fbdd 06 80 ld b,128 ;size of CP/M sector fbdf 7e wr_sector_loop: ld a,(hl) ;get byte from memory fbe0 12 ld (de),a ;put in host buffer fbe1 23 inc hl fbe2 13 inc de fbe3 10 fa djnz wr_sector_loop ;put 128 bytes in host buffer fbe5 21 72 fd ld hl,hstbuf ;location of data to write to disk fbe8 db 0f wr_status_loop_1: in a,(0fh) ;check status fbea e6 80 and 80h ;check BSY bit fbec c2 e8 fb jp nz,wr_status_loop_1 ;loop until not busy fbef db 0f wr_status_loop_2: in a,(0fh) ;check status fbf1 e6 40 and 40h ;check DRDY bit fbf3 ca ef fb jp z,wr_status_loop_2 ;loop until ready fbf6 3e 01 ld a,01h ;number of sectors = 1 fbf8 d3 0a out (0ah),a ;sector count register fbfa 3a 31 fc ld a,(sector) fbfd d3 0b out (0bh),a ;lba bits 0 - 7 = "sector" fbff 3a 2f fc ld a,(track) fc02 d3 0c out (0ch),a ;lba bits 8 - 15 = "track" fc04 3a 35 fc ld a,(diskno) fc07 d3 0d out (0dh),a ;lba bits 16 - 23, use 16 to 20 for "disk" fc09 3e e0 ld a,11100000b ;LBA mode, select drive 0 fc0b d3 0e out (0eh),a ;drive/head register fc0d 3e 30 ld a,30h ;Write sector command fc0f d3 0f out (0fh),a fc11 db 0f wr_wait_for_DRQ_set: in a,(0fh) ;read status fc13 e6 08 and 08h ;DRQ bit fc15 ca 11 fc jp z,wr_wait_for_DRQ_set ;loop until bit set fc18 7e write_loop: ld a,(hl) fc19 d3 08 out (08h),a ;write data fc1b 23 inc hl fc1c db 0f in a,(0fh) ;read status fc1e e6 08 and 08h ;check DRQ bit fc20 c2 18 fc jp nz,write_loop ;write until bit cleared fc23 db 0f wr_wait_for_BSY_clear: in a,(0fh) fc25 e6 80 and 80h fc27 c2 23 fc jp nz,wr_wait_for_BSY_clear fc2a db 0f in a,(0fh) ;clear INTRQ fc2c e6 01 and 01h ;check for error fc2e c9 ret fc2f ; fc2f ; the remainder of the cbios is reserved uninitialized fc2f ; data area, and does not need to be a Part of the fc2f ; system memory image (the space must be available, fc2f ; however, between"begdat" and"enddat"). fc2f ; fc2f 00... track: defs 2 ;two bytes for expansion fc31 00... sector: defs 2 ;two bytes for expansion fc33 00... dmaad: defs 2 ;direct memory address fc35 00... diskno: defs 1 ;disk number 0-15 fc36 ; fc36 ; scratch ram area for bdos use fc36 begdat: equ $ ;beginning of data area fc36 00... dirbf: defs 128 ;scratch directory area fcb6 00... all00: defs 31 ;allocation vector 0 fcd5 00... all01: defs 31 ;allocation vector 1 fcf4 00... all02: defs 31 ;allocation vector 2 fd13 00... all03: defs 31 ;allocation vector 3 fd32 00... chk00: defs 16 ;check vector 0 fd42 00... chk01: defs 16 ;check vector 1 fd52 00... chk02: defs 16 ;check vector 2 fd62 00... chk03: defs 16 ;check vector 3 fd72 ; fd72 enddat: equ $ ;end of data area fd72 datsiz: equ $-begdat; ;size of data area fd72 00... hstbuf: ds 256 ;buffer for host disk sector fe72 end # End of file z80_cbios.asm fe72