maclib ldropts.lib maclib cpm3.lib cseg extrn phex16, phex8 extrn cin, cout extrn crlf, crlf2 include hbios.z80 debug equ false ; BIOS Jump vector. ; All BIOS routines are invoked by calling these ; entry points. ?boot: jp boot ; initial entry on cold start ?wboot: jp wboot ; reentry on program exit, warm start ?const: jp const ; return console input status ?conin: jp conin ; return console input character ?cono: jp conout ; send console output character ?list: jp list ; send list output character ?auxo: jp auxout ; send auxilliary output character ?auxi: jp auxin ; return auxilliary input character ?home: jp home ; set disks to logical home ?sldsk: jp seldsk ; select disk drive, return disk parameter info ?sttrk: jp settrk ; set disk track ?stsec: jp setsec ; set disk sector ?stdma: jp setdma ; set disk I/O memory address ?read: jp read ; read physical block(s) ?write: jp write ; write physical block(s) ?lists: jp listst ; return list device status ?sctrn: jp sectrn ; translate logical to physical sector ?conos: jp conost ; return console output status ?auxis: jp auxist ; return aux input status ?auxos: jp auxost ; return aux output status ?dvtbl: jp devtbl ; return address of device def table ?devin: jp devini ; change baud rate of device ?drtbl: jp drvtbl ; return address of disk drive table ?mltio: jp multio ; set multiple record count for disk I/O ?flush: jp flush ; flush BIOS maintained disk caching ?mov: jp move ; block move memory to memory ?tim: jp time ; Signal Time and Date operation ?bnksl: jp selmem ; select bank for code execution and default DMA ?stbnk: jp setbnk ; select different bank for disk I/O DMA operations. ?xmov: jp xmove ; set source and destination banks for one operation jp 0 ; reserved for future expansion jp 0 ; reserved for future expansion jp 0 ; reserved for future expansion mbrsec equ dtabuf boot: ; The main module (cpmldr.asm) does not expect the ; boot call to use much stack. We use our own during ; this routine to avoid issues. ld (stksav),sp ld sp,stack ; Do the real work call boot0 ; Restore original stack and return ld sp,(stksav) ret boot0: if cmdline boot1: ; ; The following Code is the Entry Point for the Loader when ; it is built as a COM file. Typically CPMLDR.COM or ZPMLDR.COM ; ; Get disk unit from user ld de,msgunit ; disk unit prompt call writestr ; display on console call cin ; get a character push af ; save it call cout ; echo character pop af ; restore it sub '0' ; convert to binary ld (unit),a ; save it jr c,selerr ; loop if below 0 entered ld bc,BC_SYSGET_DIOCNT ; HBIOS, get disk unit count call 0FFF0h ; do it, E := disk unit count ld a,(unit) ; get unit num back cp e ; compare to entry jr nc,selerr ; loop if too high ; Get disk slice from user ld de,msgslc ; slice prompt call writestr ; display on console call cin ; get a character push af ; save it call cout ; echo it pop af ; restore it sub '0' ; convert to binary ld (slice),a ; save it jr c,selerr ; loop if below 0 entered cp 10 ; check for over 9 jr nc,selerr ; loop if over 9 ld de,msgcrlf ; linefeed call writestr ; ... to console jr boot2 ; boot w/ unit & slice selerr: ; Display invalid entry message and restart ld de,msginv ; error message call writestr ; display on console jr boot1 ; loop boot2: ; Record unit & slice w/ HBIOS ld bc,BC_SYSSET_BOOTINFO ; HBIOS func: set boot info ld a,(unit) ; get unit ld d,a ; put in D ld a,(slice) ; get slice ld e,a ; put in E ld l,0 ; Bank is always zero call 0FFF0h ; do it else ; ; The following Code is the Entry Point for the Loader when ; it is built as a SYS file, and run from the System Track ; ; Get unit & slice from HBIOS ld bc,BC_SYSGET_BOOTINFO ; HBIOS func: get boot info call 0FFF0h ; do it, D := boot unit, E: := slice ld a,d ; move unit to A ld (unit),a ; save it ld a,e ; move slice to A ld (slice),a ; save it endif ; Sense media to determine media format ld a,(unit) ; Get boot unit ld d,a ; put in D ld a,(slice) ; get boot slice ld e,a ; put in E ld b,BF_EXTSLICE ; HBIOS FUNC: SLICE call 0FFF0H ; HBIOS call ; Check errors from the Function cp ERR_NOUNIT ; compare to no unit error jp z,err_nodisk ; handle no disk err cp ERR_NOMEDIA ; no media in the device jp z,err_nodisk ; handle the error cp ERR_RANGE ; slice is invalid jp z,err_noslice ; bad slice, handle err or a ; any other error jp nz,err_diskio ; handle as general IO error ; Initialize slice start LBA and Media ID ld a,c ld (medid),a ; save media id boot8: ld (lba),hl ; save new lba, low word ld (lba+2),de ; save new lba, high word boot9: ; Locate DPB corresponding to media id ld hl,dpb$start - dpb$sz ld de,dpb$sz ld a,(medid) ; get media id ld b,a ; to loop count boot10: add hl,de ; next dpb djnz boot10 ; loop as needed ; Stuff DPB ptr (HL) into DPH ld de,dph0 ; load DPH pointer ex de,hl ; de = DPB adr, hl = DPH adr push de ; save DPB adr ld de,12 ; offset of DPB in DPH add hl,de ; hl = adr of DPB field in DPH pop de ; recover DPB adr ld (hl),e ; update LSB inc hl ; point to MSB ld (hl),d ; update MSB ret ; done wboot: ld a,81H halt const: ld a,82H halt conin: ld bc,0080h ; unit 80h (console), func 0 = CIN call 0FFF0h ; do it ld a,e ; put in C ret ; done conout: ld e,c ; output character in E ld bc,0180h ; unit 80h (console), func 1 = COUT jp 0FFF0h list: ld a,85H halt auxout: ld a,86H halt auxin: ld a,87H halt home: ld hl,0 ld (trk),hl ret seldsk: ld hl,dph0 ret settrk: ld (trk),bc ret setsec: ld (sect),bc ret setdma: if debug push hl push bc pop hl call crlf call phex16 pop hl endif ld (dma),bc ret read: if debug call crlf ld a,(unit) call phex8 ld a,' ' call cout ld hl,(trk) call phex16 ld a,' ' call cout ld hl,(sect) call phex16 ld a,' ' call cout ld hl,(lba+2) call phex16 ld hl,(lba+0) call phex16 endif ; Check device type ld a,(unit) ; get unit ld c,a ; BIOS Disk Unit in C ld b,BF_DIODEVICE ; HBIOS DEVICE function rst 08 ; Do it, D=device type ld a,d ; put in accum cp 01h ; floppy? jr nz,read2 ; if not, do LBA i/o ; Floppy I/O ld de,(sect) ; sector -> de, head(d) becomes zero ld hl,(trk) ; track -> hl (low bit has head) srl h ; shift head bit out of hl rr l ; ... and into carry rl d ; carry bit (head) into d jr read3 ; do the disk i/o ; LBA I/O read2: ld hl,(trk) ; get track ld de,0 ; clear hiword ld b,4 ; x16 (16 spt assumed) call rl32 ; do it ; combine with sector ld a,(sect) ; get sector or l ; combine ld l,a ; and back to L ; add in lba offset ld bc,(lba) ; lba offset loword add hl,bc ; add to cur loword ex de,hl ; swap ld bc,(lba+2) ; lba offset hiword adc hl,bc ; add w/ carry to cur hiword ex de,hl ; swap back set 7,d ; set lba access bit read3: if debug ld a,' ' call cout ex de,hl call phex16 ex de,hl call phex16 endif ; DE:HL has sector address to read (LBA or CHS) ld a,(unit) ; get disk unit ld c,a ; put in C ld b,1 ; read 1 sector jr diskread ; read sector and return diskread: ; Read disk sector(s) ; DE:HL is LBA, B is sector count, C is disk unit ; Seek to requested sector in DE:HL push bc ; save unit & count ld b,BF_DIOSEEK ; HBIOS func: seek call 0FFF0h ; do it pop bc ; recover unit & count jp nz,err_diskio ; handle error ; Read sector(s) into buffer ld e,b ; transfer count ld b,BF_DIOREAD ; HBIOS func: disk read ld hl,(dma) ; read into info sec buffer ld a,(0FFE0h) ; get current bank ld d,a ; put in D call 0FFF0h ; do it jp nz,err_diskio ; handle error xor a ; signal success ret ; and done write: ld a,8EH halt listst: ld a,8FH halt sectrn: ld h,b ld l,c ret conost: ld a,91H halt auxist: ld a,92H halt auxost: ld a,93H halt devtbl: ld a,94H halt devini: ld a,95H halt drvtbl: ld a,96H halt multio: ld a,97H halt flush: ld a,98H halt move: ; On input, DE=src, HL=dest ex de,hl ; swap HL/DE for LDIR ldir ; Z80 block move ex de,hl ; swap back (required!) ret ; done time: ld a,9AH halt selmem: ld a,9BH halt setbnk: ld a,9CH halt xmove: ld a,9DH halt ;cin: ; ; Input character from console via HBIOS ; ld c,080H ; console unit to C ; ld b,BF_CIOIN ; HBIOS func: input char ; call 0FFF0H ; HBIOS reads character ; ld a,e ; To A for return ; ret ; done ;cout: ; ; Output character to console via HBIOS ; ld e,a ; output char to E ; ld c,080H ; console unit to C ; ld b,BF_CIOOUT ; HBIOS func: output char ; jp 0FFF0H ; output & return writestr: push af writestr1: ld a,(de) cp '$' ; test for string terminator jp z,writestr2 push de call cout pop de inc de jp writestr1 writestr2: pop af ret rl32: ; Left shift DE:HL by B bits (B > 0) or a ; clear carry rl l ; rotate L thru carry rl h ; rotate H thru carry rl e ; rotate E thru carry rl d ; rotate D thru carry djnz rl32 ; loop B times ret ; done err_nodisk: ld hl,str_err_nodisk jr err err_noslice: ld hl,str_err_noslice jr err err_diskio: ld hl,str_err_diskio jr err err_sig: ld hl,str_err_sig jr err err_api: ld hl,str_err_api jr err err: push hl ld de,str_err_prefix call writestr pop de call writestr halt str_err_prefix db "\r\n\r\n*** ","$" str_err_nodisk db "Disk unit not available","$" str_err_noslice db "Slice not supported","$" str_err_diskio db "Disk I/O failure","$" str_err_sig db "No system image on disk","$" str_err_api db "HBIOS API failure","$" msgunit db 13,10,13,10,'Boot CP/M 3 from Disk Unit: $' msgslc db ' Slice: $' msginv db 13,10,13,10,'*** Invalid Selection ***$' msgcrlf db 13,10,'$' dpb$start: dpb$rom: ; 384K ROM Drive dw 64 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 1 ; exm: extent mask dw 192 - 1 ; dsm: total storage in blocks - 1 = (384kb / 2k bls) - 1 = 191 dw 256 - 1 ; drm: dir entries - 1 = 256 - 1 = 255 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 8000h ; cks: directory check vector size - permanent storage = 8000H dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb$sz equ $ - dpb$start dpb$ram: ; 256K RAM Drive dw 64 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 1 ; exm: extent mask dw 128 - 1 ; dsm: total storage in blocks - 1 = (256kb / 2k bls) - 1 = 127 dw 256 - 1 ; drm: dir entries - 1 = 256 - 1 = 255 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 8000h ; cks: directory check vector size - permanent storage = 8000H dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb$rf: ; 4MB RAM Floppy Drive dw 64 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 0 ; exm: extent mask dw 2047 ; dsm: total storage in blocks - 1 = (4mb / 2k bls) - 1 = 2047 dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 8000h ; cks: directory check vector size - permanent storage = 8000H dw 0 ; off: reserved tracks = 0 trks db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb$hd: ; 8MB Hard Disk Drive dw 64 ; spt: sectors per track db 5 ; bsh: block shift factor db 31 ; blm: block mask db 1 ; exm: extent mask dw 2047 ; dsm: total storage in blocks - 1 = (8mb / 4k bls) - 1 = 2047 dw 512-1 ; drm: dir entries - 1 = 512 - 1 = 511 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 8000h ; cks: directory check vector size - permanent storage = 8000H dw 16 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb$fd720: ; 3.5" DS/DD Floppy Drive (720K) dw 36 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 0 ; exm: extent mask dw 350 ; dsm: total storage in blocks - 1 blk = ((720k - 18k off) / 2k bls) - 1 = 350 dw 127 ; drm: dir entries - 1 = 128 - 1 = 127 db 11000000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 32 ; cks: directory check vector size = 128 / 4 dw 4 ; off: reserved tracks = 4 trks * (512 b/sec * 36 sec/trk) = 18k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb_fd144: ; 3.5" DS/HD Floppy Drive (1.44M) dw 72 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 0 ; exm: extent mask dw 710 ; dsm: total storage in blocks - 1 blk = ((1,440k - 18k off) / 2k bls) - 1 = 710 dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 64 ; cks: directory check vector size = 256 / 4 dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 72 sec/trk) = 18k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb_fd360: ; 5.25" DS/DD Floppy Drive (360K) dw 36 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 1 ; exm: extent mask dw 170 ; dsm: total storage in blocks - 1 blk = ((360k - 18k off) / 2k bls) - 1 = 170 dw 127 ; drm: dir entries - 1 = 128 - 1 = 127 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 32 ; cks: directory check vector size = 128 / 4 dw 4 ; off: reserved tracks = 4 trks * (512 b/sec * 36 sec/trk) = 18k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb_fd120: ; 5.25" DS/HD Floppy Drive (1.2M) dw 60 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 0 ; exm: extent mask dw 591 ; dsm: total storage in blocks - 1 blk = ((1,200k - 15k off) / 2k bls) - 1 = 591 dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 64 ; cks: directory check vector size = 256 / 4 dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 60 sec/trk) = 15k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb_fd111: ; 8" DS/DD Floppy Drive (1.11M) dw 60 ; spt: sectors per track db 4 ; bsh: block shift factor db 15 ; blm: block mask db 0 ; exm: extent mask dw 569 ; dsm: total storage in blocks - 1 blk = ((1,155k - 15k off) / 2k bls) - 1 = 569 dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 db 11110000b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 64 ; cks: directory check vector size = 256 / 4 dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 60 sec/trk) = 15k db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dpb$hdnew: ; 8MB Hard Disk Drive (new format) dw 64 ; spt: sectors per track db 5 ; bsh: block shift factor db 31 ; blm: block mask db 1 ; exm: extent mask dw 2048 - 1 - 4 ; dsm: total storage in blocks - 1 = 2048 - 1 - resvd tracks dw 1024 - 1 ; drm: dir entries db 11111111b ; al0: dir blk bit map, first byte db 00000000b ; al1: dir blk bit map, second byte dw 8000h ; cks: directory check vector size - permanent storage = 8000H dw 2 ; off: reserved tracks db 2 ; psh: 2 for 512 byte sectors db 3 ; phm: (512 / 128) - 1 dph0: dw 0 ; xlt, 0 means no translation db 0,0,0,0,0,0,0,0,0 ; scratch (9 bytes) db 0 ; mf: media flag dw dpb$hd ; dpb dw csvbuf ; csv: dw alvbuf ; alv: dw dirbcb ; dirbcb dw dtabcb ; dtabcb dw 0ffffh ; hash (disabled) db 0 ; hbank dtbl: dtbl dph0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 dirbcb: db 0ffh ; drv db 0,0,0 ; rec# db 0 ; wflg db 0 ; scratch dw 0 ; track dw 0 ; sector dw dirbuf ; buffad dtabcb: db 0ffh ; drv db 0,0,0 ; rec# db 0 ; wflg db 0 ; scratch dw 0 ; track dw 0 ; sector dw dtabuf ; buffad unit ds 1 ; HBIOS unit number slice ds 1 ; HBIOS slice number trk ds 2 ; current track sect ds 2 ; current sector dma ds 2 ; current DMA address medid ds 1 ; media id lba ds 4 ; current lba ds 64 stack equ $ stksav ds 2 csvbufs equ 256 alvbufs equ 512 dirbufs equ 512 dtabufs equ 512 ;csvbuf ds 256 ; length (CSV) = ((DRM+1)/4) ;alvbuf ds 512 ; length (ALV) = ((DSM+1)/4) ;dirbuf ds 512 ; sector buffer ;dtabuf ds 512 ; sector buffer ; Trying to save space. The loader must fit underneath ; the start of BNKBDOS3 and we have a big BNKBDOS because ; of all the disk allocations. Putting these buffers ; in upper memory actually helps quite a bit. csvbuf equ 8000h alvbuf equ csvbuf + csvbufs dirbuf equ alvbuf + alvbufs dtabuf equ dirbuf + dirbufs end