;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; @(#)bios.asm 1.13 11/11/13 ; ; Custom BIOS for a Quasitronics STD BUS ; system with an IDE hard drive. BIOS ; supports disk volumes of up to 65536 ; 16K blocks (1 GB). Each MB of disk ; requires 8 bytes of RAM, however, ; which becomes the limiting factor. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .area BIOS (ABS) .list (me) .include "globals.asm" .include "diskdef.lib" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Define constants. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; vers .equ 22 ; CP/M version number bias .equ (msize-20)*1024 ; CP/M version memory size in kilobytes ccp .equ moff+bias ; base of ccp bdos .equ ccp+0x800 ; base of bdos bdose .equ bdos+6 ; bdos entry point bios .equ ccp+0x1600 ; base of bios iobyte .equ 0x0003 ; address of Intel i/o byte cdisk .equ 0x0004 ; address of current disk number 0=A,...,15=P nsects .equ (bios-ccp)/128 ; warm start sector count buff .equ 0x0080 ; sector buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Utility macro to compute sector mask. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .macro smask hblk ;; compute log2(hblk), return $x as result ;; (2 ** $x = hblk on return) $y = hblk $x = 0 ;; count right shifts of $y until = 1 .rept 8 .if eq,$y - 1 .mexit .endif ;; $y is not 1, shift right one position $y = $y >> 1 $x = $x + 1 .endm .endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Device constants. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; punadr .equ 0x0c ; punch I/O address rdradr .equ punadr ; reader I/O address _RTS .equ 0B00000010 ; Request To Send _DTR .equ 0B00000001 ; Data Terminal Ready MODEM .equ _DTR+_RTS ; both lines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CP/M to host disk constants. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cpmfsc = 0 ; CP/M first sector number cpmspt = 256 ; CP/M sectors/track hstblk = hstsiz / 128 ; CP/M sectors/host buffer hstspt = cpmspt*128/hstsiz ; host disk sectors/track secmsk = hstblk-1 ; sector mask smask hstblk ; calculate the sector mask secshf = $x ; log2(hstblk) smask hstspt ; calculate the track mask trkshf = $x ; log2(hstspt) trkoff = 2 ; tracks to reserve for OS dsktrk = blksiz / hstsiz * dsksiz / hstspt ; tracks in each disk ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; BDOS constants on entry to write ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; wrall = 0 ; write to allocated wrdir = 1 ; write to directory wrual = 2 ; write to unallocated .org bios ; origin of this portion of code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Jump vector for individual subroutines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 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 head to home position 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 .if dirinit jp initdir ; initialize a disk directory .endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Fixed data tables for each drive. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; disks nparts ; define the disks in equal sizes dsk = 0 .rept ndisks diskdef \dsk,cpmfsc,cpmspt-1,,blksiz,dsksiz,dirent,0,trkoff+dsktrk*dsk dsk = dsk+1 .endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Startup Banner ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; signon: .db 13,10,msize/10+'0,msize%10+'0 .ascii 'k CP/M vers ' .db vers/10+'0,'.,vers%10+'0,'i,0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Cold boot. ; ; Perform parameter initialization then ; go run the OS. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; boot: ld sp,#buff+0x80 ; initialize the stack ld hl,#signon ; get the signon message call _puts ; print it xor a,a ; A = 0 ld (hstact),a ; host buffer inactive ld (unacnt),a ; clear unalloc count ld (iobyte),a ; clear the iobyte ld (cdisk),a ; select disk zero ld b,#MODEM ; get the modem control lines ld c,#punadr ; get the punch address call _spio13_sctrl ; initialize the punch device jr gocpm ; initialize and go to CP/M ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Warm boot. ; ; Reload the CCP & BDOS from disk. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; wboot: ld sp,#buff ; use space below buffer for stack xor a,a ; A = 0 ld (hstact),a ; host buffer inactive ld (unacnt),a ; clear unalloc count ld c,a ; select disk 0 call seldsk ; call home ; go to track 00 ld c,#0 ; C has the current track number ld d,#1 ; D has the next sector to read ld b,#nsects ; B counts # of sectors to load ld hl,#ccp ; base of cp/m (initial load point) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Load one more sector. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; load1: push bc ; save sector count, current track push de ; save next sector to read push hl ; save dma address ld c,d ; get sector address to register C call setsec ; set sector address from register C pop bc ; load dma address to b,c push bc ; replace on stack for later recall call setdma ; set dma address from b,c call read ; read a sector into memory or a,a ; any errors? jr nz,wboot ; yes, reboot if an error occurs pop hl ; no, recall dma address ld de,#128 ; dma=dma+sector size add hl,de ; new dma address is in HL pop de ; recall sector address pop bc ; recall number of sectors left and current trk dec b ; decrement the sector count jr z,gocpm ; transfer to cp/m if all have been loaded inc d ; otherwise, skip to the next sector ld a,#cpmspt-1 ; get last sector in track cp a,d ; end of track? jr nc,load1 ; no, load another one ld d,#0 ; yes, begin with first sector of next track inc c ; skip to the next track push bc ; save these ld b,#0 ; convert track to 16 bits call settrk ; set track from register c pop bc ; jr load1 ; load another sector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; End of load operation. Set parameters ; and go to CP/M. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; gocpm: di ; disable interrupts ld bc,#buff ; set the default DMA address call setdma ; set the DMA address ld a,#0xc3 ; C3 is a jump instruction ld (0),a ; for jump to wboot ld hl,#wboote ; wboot entry point ld (1),hl ; set address field for jump at 0 ld (5),a ; for jump to bdos ld hl,#bdose ; bdos entry point ld (6),hl ; address field of jump at 5 to bdos ld a,(cdisk) ; get current disk number ld c,a ; send to the CCP ;ei ; enable the interrupt system jp ccp ; go to CP/M for further processing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Return a character from the console. ; ; Parameters: ; None ; ; Returns: ; A = 0 for no data ; A = 0xFF for data available ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; const: call _con_rxstat ; get the console status ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Return a character from the console. ; ; Parameters: ; None ; ; Returns: ; A = new character ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; conin: call _getchar ; get a character and a,#0x7f ; strip parity bit ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Send a character to the console. ; ; Parameters: ; C = character ; ; Returns: ; Clobbers A ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; conout: ld a,c ; move character to A call _putchar ; print the character ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Get the status of the list device. ; ; Parameters: ; None. ; ; Returns: ; A = 0 not ready ; A = 1 ready ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; listst: xor a,a ; never ready ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Send a character to the list device. ; ; Parameters: ; C = character ; ; Returns: ; Nothing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list: ret ; do nothing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Send a character to the punch device. ; ; Parameters: ; C = character ; ; Returns: ; Clobbers A, B & C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; punch: ld a,c ; move character to A ld c,#punadr ; get the I/O address call _spio13_swrite ; write the character ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Get a character from the reader device. ; ; Parameters: ; None. ; ; Returns: ; A = new character ; Clobbers C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; reader: ld c,#rdradr ; get the I/O address call _spio13_sread ; read a character ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Select a new disk. ; ; Parameters: ; C = new disk ; ; Returns: ; HL = disk parameter block or 0 ; Clobbers A, D, E ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; seldsk: ld hl,#0 ; error return code ld a,c ; move disk to A cp a,#ndisks ; disk number in range? ret nc ; no, leave. ld (sekdsk),a ; yesm save the disk number ld l,a ; convert to 16 bits ld h,#0 ; .rept 4 ; multiply by 16 (size of each header) add hl,hl ; *2 .endm ; ld de,#dpbase ; get the drive parameter location add hl,de ; HL=.dpbase(sekdsk*16) ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Position the drive head on track 0. ; ; Parameters: ; None ; ; Returns: ; Clobbers A, B, C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; home: ld a,(hstwrt) ; get host written flag or a,a ; host written? jr nz,homed ; yes, reset to track 0 ld (hstact),a ; no, clear host active flag homed: ld bc,#0 ; track 0 ;call settrk ; set to track 0 ;ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Set the current track. ; ; Parameters: ; BC = new track ; ; Returns: ; Nothing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; settrk: ld (sektrk),bc ; save track ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Set the current sector. ; ; Parameters: ; BC = new sector ; ; Returns: ; Clobbers A ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; setsec: ld a,c ; get the LSB of the sector number ld (seksec),a ; save only this portion ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Translate a sector. ; ; Parameters: ; BC = sector ; DE = translation table ; ; Returns: ; HL = new sector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; sectran: ld h,b ; HL=BC ld l,c ; ret ; done. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Set the DMA address. ; ; Parameters: ; BC = new address ; ; Returns: ; Nothing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; setdma: ld (dmaadr),bc ; save the address ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Read a sector from disk with deblocking. ; ; Parameters: ; sekdsk = current disk ; sektrk = current track ; seksec = sector to read ; ; Returns: ; A = error code ; Clobbers everything ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; read: xor a,a ; A = 0 ld (unacnt),a ; clear unalloc count ld a,#1 ; true flag ld (readop),a ; read operation = true ld (rsflag),a ; must read data = true ld a,#wrual ; ld (wrtype),a ; treat as unalloc jr rwoper ; perform the read ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Write a sector to disk with deblocking. ; ; Parameters: ; C = write type ; sekdsk = current disk ; sektrk = current track ; seksec = sector to write ; ; Returns: ; A = error code ; Clobbers everything ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; write: xor a,a ; clear A ld (readop),a ; not a read operation ld a,c ; write type in C ld (wrtype),a ; save it cp a,#wrual ; write unallocated? jr nz,chkuna ; no, check for unallocated ; write to unallocated, set parameters ld a,#blksiz/128 ; next unallocated records ld (unacnt),a ; save it ld a,(sekdsk) ; disk to seek ld (unadsk),a ; set unadsk ld hl,(sektrk) ; get the track ld (unatrk),hl ; set unatrk ld a,(seksec) ; get sector ld (unasec),a ; set unasec chkuna: ; check for write to unallocated sector ld a,(unacnt) ; get unallocated count or a,a ; any unallocated remaining? jr z,alloc ; no, skip this ; more unallocated records remain dec a ; decrement unallocated count ld (unacnt),a ; save it ld a,(sekdsk) ; get disk number ld hl,#unadsk ; get unadsk address cp a,(hl) ; same disk? jr nz,alloc ; no, skip this ; disks are the same ld hl,#unatrk ; unallocated track address call sektrkcmp ; same track? jr nz,alloc ; no, skip this ; tracks are the same ld a,(seksec) ; get the sector ld hl,#unasec ; unallocated sector address cp a,(hl) ; same sector? jr nz,alloc ; no skip this ; match, move to next sector for future reference inc (hl) ; next sector ld a,#cpmspt-1 ; get last sector in track cp a,(hl) ; end of track? jr nc,noovf ; skip of no overflow ; overflow to next track ld (hl),#0 ; unasec = 0 ld hl,(unatrk) ; get unallocated track inc hl ; next track ld (unatrk),hl ; save it noovf: ; match found, mark as unnecessary read xor a,a ; clear A ld (rsflag),a ; clear rsflag jr rwoper ; perform the write alloc: ; not an unallocated record, requires pre-read xor a,a ; clear A ld (unacnt),a ; clear unallocated count inc a ; true ld (rsflag),a ; set rsflag ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Common code for read and write follows. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; rwoper: ; enter here to perform the read/write xor a,a ; clear A ld (erflag),a ; no errors (yet) ld a,(seksec) ; get sector .rept secshf ; clear carry & compute the host sector or a,a ; rra ; / 2 .endm ld (sekhst),a ; host sector to seek ; active host sector? ld hl,#hstact ; get address to host active flag ld a,(hl) ; get flag ld (hl),#1 ; always set flag or a,a ; was it already set? jr z,filhst ; no, fill host buffer ; host buffer active, same as seek buffer? ld a,(sekdsk) ; get disk number ld hl,#hstdsk ; get host disk address cp a,(hl) ; same disk? jr nz,nomatch ; no, clear host buffer first ; same disk, same track? ld hl,#hsttrk ; get host track address call sektrkcmp ; same track? jr nz,nomatch ; no, clear host buffer first ; same disk, same track, same buffer? ld a,(sekhst) ; get host sector to seek ld hl,#hstsec ; get host sector address cp a,(hl) ; same sector? jr z,match ; yes, skip this nomatch: ; proper disk, but not correct sector ld a,(hstwrt) ; get host written flag or a,a ; host written? call nz,writehst ; if not, clear host buffer filhst: ; may have to fill the host buffer ld a,(sekdsk) ; get disk number ld (hstdsk),a ; set host disk ld hl,(sektrk) ; get track number ld (hsttrk),hl ; set host track ld a,(sekhst) ; get host sector to seek ld (hstsec),a ; set host sector ld a,(rsflag) ; get read flag or a,a ; need to read? call nz,readhst ; yes, read a sector from the drive xor a,a ; clear A ld (hstwrt),a ; no pending write match: ; copy data to or from buffer ld a,(seksec) ; get sector number and a,#secmsk ; mask buffer number LS bits ld l,a ; convert to 16 bits ld h,#0 ; .rept 7 ; shift left 7 times add hl,hl ; shift left .endm ; ld de,#hstbuf ; get host buffer add hl,de ; HL now host address ld de,(dmaadr) ; get the DMA address ld bc,#128 ; length of move ld a,(readop) ; get read flag or a,a ; move which way? jr nz,rwmove ; skip this if read ; write operation, mark and switch direction ld a,#1 ; write flag ld (hstwrt),a ; set host write flag ex de,hl ; swap source and destination rwmove: ; BC initially 128, HL is source, DE is destination ldir ; transfer data ; data has been moved to/from host buffer ld a,(wrtype) ; get write type cp a,#wrdir ; to directory? ld a,(erflag) ; in case of errors ret nz ; no further processing ; clear host buffer for directory write or a,a ; errors? ret nz ; yes, leave xor a,a ; clear A ld (hstwrt),a ; buffer written call writehst ; write to drive ld a,(erflag) ; get error flag ret ; leave ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Utility subroutine for 16-bit compare ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; sektrkcmp: ; HL = .unatrk or .hsttrk, compare with track ex de,hl ; move track address to DE ld hl,#sektrk ; get seek track number ld a,(de) ; get LSB of host track cp a,(hl) ; same? ret nz ; no, leave inc de ; high byte inc hl ; ld a,(de) ; get LSB of host track cp a,(hl) ; set flags ret ; done ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Perform the physical write ; ; Parameters: ; hstbuf = buffer to write from ; ; Returns: ; erflag = non-zero if error ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; writehst: call getlba ; get lba in B,DE,A and buffer in HL call _ide_write ; write the sector to disk ld (erflag),a ; store any error codes ret ; done. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Perform the physical read ; ; Parameters: ; hstbuf = buffer to read to ; ; Returns: ; erflag = non-zero if error ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; readhst: call getlba ; get lba in B,DE,A and buffer in HL call _ide_read ; load the sector into memory ld (erflag),a ; store any error codes ret ; done. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Caclculate the LBA from the disk, track, ; and sector values. ; ; LBA = hsttrk << log2(hstspt) + hstsec + llbase ; ; Parameters: ; hsttrk = host track # ; hstsec = host sector # ; hstsiz = bytes to read ; ; Returns: ; A,DE,B = host LBA ; HL = host buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; getlba: ld hl,(hsttrk) ; get the track number ld de,#0 ; convert to 32 bits ld b,#trkshf ; get the number of bits in a sector 1$: sla l ; convert the track to sectors rl h ; rl e ; rl d ; djnz 1$ ; loop if not done ld a,(hstsec) ; get the sector number or a,l ; add it to the sector count ld l,a ; save in back in sector count ld bc,(lbbase) ; get the low word of the LBA add hl,bc ; add to the sector count ld bc,(lbbase+2) ; get the high word of the lba ex de,hl ; swap words adc hl,bc ; add to the sector count ex de,hl ; swap back ; ; finish up ; ld a,d ; rearrange LBA for the IDE read/write routines ld d,e ; ld e,h ; ld b,l ; ld c,#1 ; process only one sector ld hl,#hstbuf ; get the buffer address ret ; all set for readhst or writehst .if dirinit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Initialize a disk directory. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; initdir:: ; ; Get a valid drive letter. ; ld hl,#msg1 ; first portion of the prompt call _put ; print it ld a,#ndisks ; get number of disks add a,#'@ ; convert to drive letter call _putchar ; print it ld hl,#msg2 ; rest of the prompt call _put ; print it ld hl,#buff ; get the keyboard buffer address call _gets ; ask for a drive letter 1$: ld a,(hl) ; get the next character in the buffer cp a,#0x20 ; space? jr nz,2$ ; no, leave inc hl ; yes, skip to the next buffer position jr 1$ ; check the character 2$: or a,a ; EOL? ret z ; yes, leave and a,#0xdf ; convert drive letter to uppercase sub a,#'A ; calculate the drive number cp a,#ndisks ; out of range? ret nc ; yes, leave or a,a ; no clear carry ; ; Get the directory starting track and sector. ; ld b,a ; save a copy of the drive rla ; convert drive to DPB offset rla ; rla ; rla ; sub a,b ; add a,#13 ; location of track offset ld hl,#dpb0 ; pointer to the drive parameter blocks ld e,a ; copy offset to E ld d,#0 ; convert to 16 bits add hl,de ; add to DPB ld e,(hl) ; get low byte of track offset inc hl ; point to high byte ld d,(hl) ; get high byte ld hl,#hsttrk ; location of the host track variable ld (hl),e ; save low byte inc hl ; point to high byte ld (hl),d ; save high byte ld hl,#hstsec ; location of the host sector variable ld (hl),#0 ; start at sector 0 ; ; Fill the sector buffer with zeroes. ; ld hl,#hstbuf ; location of sector buffer ld b,#0 ; loop for 256 bytes 3$: ld (hl),#0 ; clear buffer location inc hl ; next location djnz 3$ ; loop if not done 4$: ld (hl),#0 ; clear buffer location inc hl ; next location djnz 4$ ; loop if not done ; ; Create the directory entries. ; ld de,#20 ; each entry is 32 bytes long ld hl,#hstbuf ; location of sector buffer 5$: ld (hl),#0xe5 ; store unsed directory record value inc hl ; next position ld b,#11 ; write 11 spaces (filename.ext) 6$: ld (hl),#0x20 ; write space inc hl ; next location djnz 6$ ; loop until done add hl,de ; skip to the next record position ld a,h ; end of buffer? cp a,#>(hstbuf+hstsiz) jr c,5$ ; no, continue ld a,l ; check low byte cp a,#<(hstbuf+hstsiz) jr c,5$ ; no, continue ; ; Determine how many sectors to create. ; ld hl,#dirent ; get the number of directory entries smask hstsiz/32 ; calculate the directory mask ld b,#$x ; load number of bits to shift 11$: srl h ; / 2 rr l ; djnz 11$ ; if not done, loop again push hl ; save number of sectors to write call getlba ; get the initial LBA & buffer pointer ; ; Create the directory. ; 8$: push hl ; save buffer pointer push af ; save high byte of LBA ld hl,#msg0 ; get writing block message call _put ; print it pop af ; restore high byte of LBA push af ; save a copy call _prhex ; print the LBA ld a,d ; call _prhex ; ld a,e ; call _prhex ; ld a,b ; call _prhex ; call _crlf ; print end of line pop af ; restore high byte of LBA pop hl ; restore buffer pointer push af ; save high byte of LBA call _ide_write ; write the directory block jr nz,7$ ; if error, leave pop af ; otherwise restore high byte of LBA inc b ; increment the LBA jr nz,9$ ; inc e ; jr nz,9$ ; inc d ; jr nz,9$ ; inc a ; 9$: pop hl ; get number of sectors left dec hl ; decrement the number push af ; save high byte of the LBA ld a,h ; see if we're done or a,l ; jr z,10$ ; if done, finish up pop af ; restore high byte of LBA push hl ; save sector count ld hl,#hstbuf ; get the sector buffer jr 8$ ; write another sector ; ; Error handler. ; 7$: push af ; save error code ld hl,#msg3 ; get error message call _put ; print it pop af ; get error code call _prhex ; print it call _crlf ; end of line 10$: pop af ; clean off the stack xor a,a ; return 0 ret ; leave msg0: .asciz "WRITING BLOCK " msg1: .asciz "INITIALIZE DRIVE (A-" msg2: .asciz "): " msg3: .asciz "ERROR: " .endif endbios .equ . .ifgt msize-59 ; if memory size > 59k .org 0xf880 ; then push scratch area above ROM .endif ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The remainder of the CBIOS is reserved ; uninitialized data area, and does not ; need to be a part of the system memory ; image (the space must be available, ; however, between "begdat" and "enddat"). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; lbbase::.ds 4 ; LBA of our disk partition sekdsk: .ds 1 ; seek disk number 0-15 sektrk: .ds 2 ; seek track number seksec: .ds 1 ; seek sector number hstdsk: .ds 1 ; host disk number hsttrk: .ds 2 ; host track number hstsec: .ds 1 ; host sector number sekhst: .ds 1 ; seek shr secshf hstact: .ds 1 ; host active flag hstwrt: .ds 1 ; host written flag unacnt: .ds 1 ; unalloc record count unadsk: .ds 1 ; last unallocated disk unatrk: .ds 2 ; last unallocated track unasec: .ds 1 ; last unallocated sector erflag: .ds 1 ; error reporting rsflag: .ds 1 ; read sector flag readop: .ds 1 ; 1 if read operation wrtype: .ds 1 ; write operation type dmaadr: .ds 2 ; last DMA address hstbuf: .ds hstsiz ; host buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Scratch ram area for BDOS use. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; endef .end