mirror of
https://github.com/wwarthen/RomWBW.git
synced 2026-02-06 22:33:12 -06:00
- Detect the situation where the RAM disk area is all zeroes and handle it properly as uninitialized.
793 lines
20 KiB
Z80 Assembly
793 lines
20 KiB
Z80 Assembly
title 'Boot loader module for CP/M 3.0'
|
|
|
|
maclib options.lib
|
|
|
|
public ?init,?ldccp,?rlccp,?time
|
|
public @bootdu,@bootsl
|
|
extrn ?pmsg,?conin
|
|
extrn ?mvinit,?bnkxlt,?xmove,?move
|
|
extrn @civec,@covec,@aivec,@aovec,@lovec
|
|
extrn @cbnk,?bnksl,?bank
|
|
extrn @sysdr,@ccpdr
|
|
extrn dph0
|
|
extrn @dtbl,@ctbl
|
|
extrn @date,@hour,@min,@sec
|
|
extrn @srch1
|
|
extrn @hbbio
|
|
extrn addhla
|
|
extrn phex16, phex8
|
|
extrn cin, cout
|
|
extrn crlf, crlf2
|
|
extrn bcd2bin, bin2bcd
|
|
|
|
include c:ver.lib
|
|
|
|
bdos equ 5
|
|
|
|
if banked
|
|
tpa$bank equ 1
|
|
else
|
|
tpa$bank equ 0
|
|
endif
|
|
|
|
dseg ; init done from banked memory
|
|
|
|
?init:
|
|
call ?mvinit
|
|
|
|
; Install RomWBW CBIOS stamp in page zero
|
|
ld hl,stpimg
|
|
ld de,stploc
|
|
ld bc,stpsiz
|
|
ldir
|
|
|
|
if banked
|
|
|
|
; Clone page zero from bank 0 to additional banks
|
|
ld b,4 ; last bank
|
|
ld c,0 ; src bank
|
|
init$2:
|
|
push bc ; save bank id's
|
|
call ?xmove ; set src/dest banks
|
|
ld bc,0100h ; size is one page
|
|
ld hl,0 ; dest adr is 0
|
|
ld de,0 ; src adr is 0
|
|
call ?move ; do it
|
|
pop bc ; restore bank id's
|
|
djnz init$2 ; loop till done
|
|
|
|
endif
|
|
|
|
call cinit ; char device init
|
|
ld hl,signon$msg ; signon message
|
|
call ?pmsg ; print it
|
|
|
|
; Check for HBIOS/CBIOS mismatch
|
|
ld b,0F1h ; hbios version
|
|
rst 08 ; do it, de=maj/min/up/pat
|
|
ld a,d ; a := maj/min
|
|
cp ((rmj << 4) | rmn) ; match?
|
|
jr nz,init$3 ; handle ver mismatch
|
|
ld a,e ; a := os up/pat
|
|
and 0F0h ; pat not included in match
|
|
cp (rup << 4) ; match?
|
|
jr nz,init$3 ; handle ver mismatch
|
|
jr init$4 ; all good, continue
|
|
init$3:
|
|
; display version mismatch
|
|
ld hl,vermis$msg ; version mismatch
|
|
call ?pmsg ; display it
|
|
init$4:
|
|
|
|
; Get boot disk unit and save it
|
|
ld bc,0F8E0h ; HBIOS func: get boot info
|
|
rst 08 ; do it, D := boot unit, E: := boot slice
|
|
ld a,d ; move boot unit to A
|
|
ld (@bootdu),a ; save it
|
|
ld a,e ; move boot slice to A
|
|
ld (@bootsl),a ; save it
|
|
|
|
call dinit
|
|
call clrram
|
|
ret
|
|
|
|
cinit:
|
|
; Setup CON: I/O vector based on HBIOS console device
|
|
ld b,0FAh ; HBIOS Peek Function
|
|
ld a,(@hbbio) ; HBIOS bank id
|
|
ld d,a ; ... goes in D
|
|
ld hl,112h ; Offset 112h is current console device
|
|
rst 08 ; Call HBIOS, value in E
|
|
push de ; save console unit value
|
|
ld b,e ; Use as loop counter
|
|
inc b ; ... but loop 1 extra time
|
|
ld hl,0 ; Clear vector bitmap
|
|
scf ; Set carry
|
|
cinit$1:
|
|
rr h ; Rotate carry flag
|
|
rr l ; ... into correct vector position
|
|
djnz cinit$1 ; loop as needed
|
|
|
|
ld (@civec),hl ; assign to console input
|
|
ld (@covec),hl ; assign to console output
|
|
|
|
; Setup AUX: I/O vector if there are 2+ char devices in system
|
|
ld bc,0F800h ; HBIOS GET Character Device Count
|
|
rst 08 ; do it, count in E
|
|
ld a,e ; device count to accum
|
|
pop de ; recover console unit num to E
|
|
push af ; save device count
|
|
cp 2 ; check for 2+ char devices
|
|
jr c,cinit$3 ; if not, skip aux assignment
|
|
ld a,e ; console unit num to A
|
|
or a ; check for zero
|
|
ld hl,4000h ; assume aux on second char device
|
|
jr z,cinit$2 ; if console on unit 0, assumption good
|
|
ld hl,8000h ; otherwise, aux goes to first char device
|
|
cinit$2:
|
|
ld (@aivec),hl ; assign to aux input
|
|
ld (@aovec),hl ; assign to aux output
|
|
cinit$3:
|
|
pop af ; recover device count
|
|
; Truncate char table based on actual num of char devices
|
|
rlca ; A still has char device count
|
|
rlca ; * 8 for ctbl entry size
|
|
rlca ; "
|
|
ld hl,@ctbl ; Start of char table
|
|
call addhla ; Skip used entries
|
|
xor a ; Zero to accum
|
|
ld (hl),0 ; Set table terminator
|
|
ret ; done
|
|
|
|
dinit:
|
|
; loop through all disk devices to count hard disk units
|
|
ld b,0F8h ; SYS GET
|
|
ld c,010h ; Disk Drive Unit Count
|
|
rst 08 ; e := disk unit count
|
|
ld b,e ; count to b
|
|
ld a,b ; count to a
|
|
or a ; set flags
|
|
ret z ; !!! handle zero devices (albeit poorly) !!!
|
|
|
|
; loop thru devices to count total hard disk volumes
|
|
ld c,0 ; init c as device list index
|
|
ld d,0 ; init d as total device count
|
|
ld e,0 ; init e for hard disk device count
|
|
ld hl,drvlst ; init hl ptr to drive list
|
|
;
|
|
dinit2:
|
|
call dinit3 ; check drive
|
|
inc c ; next unit
|
|
djnz dinit2 ; loop
|
|
ld a,d ; total device count to d
|
|
ld (drvlstc),a ; save the count
|
|
jr dinit4 ; continue
|
|
|
|
dinit3:
|
|
push de ; save de (hard disk volume counter)
|
|
push hl ; save drive list ptr
|
|
push bc ; save loop control
|
|
ld b,17h ; hbios func: report device info
|
|
rst 08 ; call hbios, unit to c
|
|
ld a,d ; device type to a
|
|
pop bc ; restore loop control
|
|
pop hl ; restore drive list ptr
|
|
pop de ; restore de
|
|
cp 30h ; hard disk device?
|
|
jr nc,dinit3a ; if so, handle special
|
|
ld (hl),c ; save unit num in list
|
|
inc hl ; bump ptr
|
|
inc d ; inc total device count
|
|
ret
|
|
;
|
|
dinit3a:
|
|
; check for active and return if not
|
|
push de ; save de (hard disk volume counter)
|
|
push hl ; save drive list ptr
|
|
push bc ; save loop control
|
|
|
|
ld b,18h ; hbios func: sense media
|
|
ld e,1 ; perform media discovery
|
|
rst 08
|
|
|
|
pop bc ; restore loop control
|
|
pop hl ; restore drive list ptr
|
|
pop de ; restore de
|
|
|
|
ret nz ; if no media, just return
|
|
|
|
; if active...
|
|
ld (hl),c ; save unit num in list
|
|
inc hl ; bump ptr
|
|
inc d ; inc total device count
|
|
inc e ; increment hard disk count
|
|
ret ; and return
|
|
|
|
dinit4: ; set slices per volume (hdspv) based on hard disk volume count
|
|
ld a,e ; hard disk volume count to a
|
|
ld e,8 ; assume 8 slices per volume
|
|
dec a ; dec accum to check for count = 1
|
|
jr z,dinit5 ; yes, skip ahead to implement 8 hdspv
|
|
ld e,4 ; now assume 4 slices per volume
|
|
dec a ; dec accum to check for count = 2
|
|
jr z,dinit5 ; yes, skip ahead to implement 4 hdspv
|
|
ld e,2 ; in all other cases, we use 2 hdspv
|
|
|
|
dinit5:
|
|
ld a,e ; slices per volume value to accum
|
|
ld (hdspv),a ; save it
|
|
ld hl,0 ; dph index
|
|
|
|
ld a,(@bootdu) ; boot disk unit
|
|
ld d,a ; ... to d
|
|
ld a,(@bootsl) ; boot slice
|
|
ld e,a ; ... to e
|
|
ld b,1 ; one slice please
|
|
call dinit8a ; make DPH for A:
|
|
|
|
ld a,(drvlstc) ; active drive list count to accum
|
|
ld b,a ; ... and move to b for loop counter
|
|
ld de,drvlst ; de is ptr to active drive list
|
|
|
|
dinit6:
|
|
; loop thru all units available
|
|
push de ; preserve drive list ptr
|
|
ex de,hl ; list ptr to hl
|
|
ld c,(hl) ; get unit num from list
|
|
ex de,hl ; list ptr back to de
|
|
push bc ; preserve loop control
|
|
push hl ; preserve dph pointer
|
|
ld b,17h ; hbios func: report device info
|
|
rst 08 ; call hbios, d := device type
|
|
pop hl ; restore dph pointer
|
|
pop bc ; get unit index back in c
|
|
push bc ; resave loop control
|
|
call dinit7 ; make drive map entry(s)
|
|
pop bc ; restore loop control
|
|
inc c ; increment list index
|
|
pop de ; restore drive list ptr
|
|
inc de ; increment active drive list ptr
|
|
djnz dinit6 ; loop as needed
|
|
ret
|
|
|
|
dinit7: ; process a unit (all slices)
|
|
ld e,0 ; initialize slice index
|
|
ld b,1 ; default loop counter
|
|
ld a,d ; device type to accum
|
|
ld d,c ; unit number to d
|
|
cp 030h ; hard disk device?
|
|
jr c,dinit8 ; nope, leave loop count at 1
|
|
ld a,(hdspv) ; get slices per volume to accum
|
|
ld b,a ; move to b for loop counter
|
|
|
|
dinit8: ; test to avoid reallocating boot disk unit/slice
|
|
ld a,(@bootdu) ; boot disk unit to accum
|
|
cp d ; compare to cur unit
|
|
jr nz,dinit8a ; if ne, ok to continue
|
|
ld a,(@bootsl) ; boot slice to accum
|
|
cp e ; compare to cur slice
|
|
jr nz,dinit8a ; if ne, ok to continue
|
|
inc e ; is boot du/slice, skip it
|
|
djnz dinit8 ; loop till done with unit
|
|
ret
|
|
|
|
dinit8a:
|
|
; d=unit, e=slice, l=dph#, b=slice cnt
|
|
ld a,l ; dph # to accum
|
|
cp 16 ; dph table size
|
|
ret z ; bail out if overflow
|
|
push hl ; save dph #
|
|
rlca ; *2 for adr entry
|
|
ld hl,@dtbl ; dph table start
|
|
call addhla ; offset hl to desired entry
|
|
ld a,(hl) ; dereference
|
|
inc hl
|
|
ld h,(hl)
|
|
ld l,a
|
|
dec hl ; backup to slice field
|
|
ld (hl),e ; update slice number
|
|
dec hl ; backup to unit number
|
|
ld (hl),d ; update unit number
|
|
pop hl ; restore dph #
|
|
inc hl ; next dph #
|
|
inc e ; next slice
|
|
djnz dinit8 ; loop till done with unit
|
|
ret
|
|
|
|
; RomWBW CBIOS page zero stamp starts at $40
|
|
; $40-$41: Marker ('W', ~'W')
|
|
; $42-$43: Version bytes: major/minor, update/patch
|
|
; $44-$45: CBIOS Extension Info address
|
|
|
|
stploc equ 40h
|
|
stpimg db 'W',~'W' ; marker
|
|
db rmj << 4 | rmn ; first byte of version info
|
|
db rup << 4 | rtp ; second byte of version info
|
|
dw cbx ; address of cbios ext data
|
|
stpsiz equ $ - stpimg
|
|
|
|
|
|
cseg ; ram disk init must be done from resident memory
|
|
|
|
;
|
|
; Initialize ram disk by filling directory with 'e5' bytes
|
|
; Fill first 8k of ram disk track 1 with 'e5'
|
|
;
|
|
clrram:
|
|
di ; no interrupts
|
|
ld a,(0FFE0h) ; get current bank
|
|
push af ; save it
|
|
ld b,0FAh ; HBIOS Peek Function
|
|
ld a,(@hbbio) ; HBIOS bank id
|
|
ld d,a ; ... goes in D
|
|
ld hl,1DCh ; Offset 1DCh is ram disk bank 0
|
|
rst 08 ; Call HBIOS, value in E
|
|
ld a,e ; move to A for bank sel
|
|
cp 0FFh
|
|
jr z,clrram3
|
|
;call hb_bnksel ; select bank
|
|
call 0FFF3h ; select bank
|
|
|
|
; Check the first sector (512 bytes) for all zeroes. If so,
|
|
; it implies the RAM is uninitialized.
|
|
ld hl,0 ; start at begining of ram disk
|
|
ld bc,512 ; compare 512 bytes
|
|
xor a ; compare to zero
|
|
clrram000:
|
|
cpi ; a - (hl), hl++, bc--
|
|
jr nz,clrram00 ; if not zero, go to next test
|
|
jp pe,clrram000 ; loop thru all bytes
|
|
jr clrram2 ; all zeroes, jump to init
|
|
clrram00:
|
|
; Check first 32 directory entries. If any start with an invalid
|
|
; value, init the ram disk. Valid entries are e5 (empty entry) or
|
|
; 0-15 (user number).
|
|
ld hl,0
|
|
ld de,32
|
|
ld b,32
|
|
clrram0:
|
|
ld a,(hl)
|
|
cp 0E5h
|
|
jr z,clrram1 ; e5 is valid
|
|
cp 16
|
|
jr c,clrram1 ; 0-15 is also valid
|
|
jr clrram2 ; invalid entry! jump to init
|
|
clrram1:
|
|
add hl,de ; loop for 32 entries
|
|
djnz clrram0
|
|
; jr clrram2 ; *debug*
|
|
jr clrram3 ; all entries valid, bypass init
|
|
clrram2:
|
|
ld hl,0 ; source adr for fill
|
|
ld bc,2000h ; length of fill is 8k
|
|
ld a,0E5h ; fill value
|
|
call fill ; do it
|
|
or 0ffh ; flag value for cleared
|
|
ld (clrflg),a ; save it
|
|
clrram3:
|
|
pop af ; recover original bank
|
|
;call hb_bnksel ; select bank
|
|
call 0FFF3h ; select bank
|
|
ei ; resume interrupts
|
|
|
|
ld a,(clrflg) ; get cleared flag
|
|
or a ; set flags
|
|
ld hl,clr$msg ; clear ram disk message
|
|
call nz,?pmsg ; print msg if true
|
|
|
|
ret
|
|
|
|
;
|
|
; Fill memory at hl with value a, length in bc. All regs used.
|
|
; Length *must* be greater than 1 for proper operation!!!
|
|
;
|
|
fill:
|
|
ld d,h ; set de to hl
|
|
ld e,l ; so destination equals source
|
|
ld (hl),a ; fill the first byte with desired value
|
|
inc de ; increment destination
|
|
dec bc ; decrement the count
|
|
ldir ; do the rest
|
|
ret ; return
|
|
|
|
|
|
cseg ; boot loading most be done from resident memory
|
|
|
|
; This version of the boot loader loads the CCP from a file
|
|
; called CCP.COM on the system drive.
|
|
|
|
?ldccp:
|
|
; Force CCP to use system boot drive as initial default
|
|
;ld a,(@sysdr) ; get system boot drive
|
|
;ld (@ccpdr),a ; set CCP current drive
|
|
|
|
; First time, load the CCP.COM file into TPA
|
|
;ld a,(@sysdr) ; get system boot drive
|
|
;;ld (4),a ; save in page zero???
|
|
;inc a ; drive + 1 for FCB
|
|
;ld (ccp$fcb),a ; stuff into FCB
|
|
;add 'A' - 1 ; drive letter
|
|
;ld (ccp$msg$drv),a ; save for load msg
|
|
;xor a
|
|
;ld (ccp$fcb+15),a
|
|
ld hl,0
|
|
ld (fcb$nr),hl
|
|
ld de,ccp$fcb
|
|
call open
|
|
inc a
|
|
jr z,no$CCP
|
|
ld de,0100H
|
|
call setdma
|
|
ld de,128
|
|
call setmulti
|
|
ld de,ccp$fcb
|
|
call read
|
|
|
|
if banked
|
|
|
|
ld hl,0100h ; clone 3K, just in case
|
|
ld bc,0C80h
|
|
ld a,(@cbnk) ; save current bank
|
|
push af
|
|
ld$1:
|
|
ld a,tpa$bank ; select TPA
|
|
call ?bnksl
|
|
ld a,(hl) ; get a byte
|
|
push af
|
|
ld a,2 ; select extra bank
|
|
call ?bnksl
|
|
pop af ; save the byte
|
|
ld (hl),a
|
|
inc hl ; bump pointer, drop count
|
|
dec bc
|
|
ld a,b ; test for done
|
|
or c
|
|
jr nz,ld$1
|
|
pop af ; restore original bank
|
|
call ?bnksl
|
|
|
|
endif
|
|
|
|
ret
|
|
|
|
no$CCP: ; here if we couldn't find the file
|
|
ld hl,ccp$msg
|
|
call ?pmsg
|
|
call ?conin
|
|
jp ?ldccp
|
|
|
|
|
|
?rlccp:
|
|
|
|
if banked
|
|
|
|
ld hl,0100h ; clone 3K
|
|
ld bc,0C80h
|
|
rl$1:
|
|
ld a,2 ; select extra bank
|
|
call ?bnksl
|
|
ld a,(hl) ; get a byte
|
|
push af
|
|
ld a,tpa$bank ; select TPA
|
|
call ?bnksl
|
|
pop af ; save the byte
|
|
ld (hl),a
|
|
inc hl ; bump pointer, drop count
|
|
dec bc
|
|
ld a,b ; test for done
|
|
or c
|
|
jr nz,rl$1
|
|
ret
|
|
|
|
else
|
|
|
|
jr ?ldccp
|
|
|
|
endif
|
|
|
|
?time:
|
|
; per CP/M 3 docs, *must* preserve HL, DE
|
|
push hl
|
|
push de
|
|
|
|
; force return through time$ret
|
|
ld hl,time$ret
|
|
push hl
|
|
|
|
; branch to get or set routine
|
|
ld a,c ; get switch value
|
|
or a ; test for zero
|
|
jr z,time$get ; 0 means get time
|
|
jr time$set ; else set time
|
|
|
|
time$ret:
|
|
; restore HL, DE
|
|
pop de
|
|
pop hl
|
|
ret
|
|
|
|
time$get:
|
|
; RTC -> cpm date/time in SCB
|
|
|
|
; read time from RTC
|
|
ld b,020h ; HBIOS func: get time
|
|
ld hl,tim$buf ; time buffer
|
|
rst 08 ; do it
|
|
ret nz ; bail out on error
|
|
|
|
; convert yymmss in time buffer -> cpm3 epoch date offset
|
|
call date2cpm ; time buf (yr, mon, day) -> SCB (@date)
|
|
|
|
time$get1:
|
|
; set time fields in SCB
|
|
ld a,(tim$hr) ; get hour from time buf
|
|
ld (@hour),a ; ... and put in SCB
|
|
ld a,(tim$min) ; get minute from time buf
|
|
ld (@min),a ; ... and put in SCB
|
|
ld a,(tim$sec) ; get second from time buf
|
|
ld (@sec),a ; ... and put in SCB
|
|
|
|
ret
|
|
|
|
time$set:
|
|
; CPM date/time in SCB -> RTC
|
|
|
|
; convert CPM3 epoch date offset in SCB -> yymmss in time buffer
|
|
call cpm2date ; SCB (@date) -> time buf (yr, mon, day)
|
|
|
|
; copy CPM3 time values from SCB -> time buffer
|
|
ld a,(@hour) ; get hour from SCB
|
|
ld (tim$hr),a ; ... and put in tim$hr
|
|
ld a,(@min) ; get minute from SCB
|
|
ld (tim$min),a ; ... and put in tim$min
|
|
ld a,(@sec) ; get second from SCB
|
|
ld (tim$sec),a ; ... and put in tim$sec
|
|
|
|
; send time to RTC
|
|
ld b,021h ; HBIOS func: set time
|
|
ld hl,tim$buf ; ... from time buffer
|
|
rst 08 ; do it
|
|
|
|
ret
|
|
|
|
date2cpm:
|
|
; Convert YYMMSS from time buffer at HL
|
|
; into offset from CPM epoch and store
|
|
; result in SCB.
|
|
|
|
ld hl,0 ; initialize day counter
|
|
; Add in days for elapsed years
|
|
ld a,(tim$yr) ; get current year
|
|
call bcd2bin ; convert to binary
|
|
sub 78 ; epoch year
|
|
jr nc,d2c1 ; if not negative, good to go
|
|
add a,100 ; else, adjust for Y2K wrap
|
|
d2c1:
|
|
ld b,a ; loop counter
|
|
ld c,3 ; leap counter, 78->79->80, so 3
|
|
ld de,365 ; days in non-leap year
|
|
or a ; check for zero
|
|
jr z,d2c10 ; skip if zero
|
|
d2c2:
|
|
add hl,de ; add non-leap days
|
|
dec c ; dec leap counter
|
|
jr nz,d2c3 ; if not leap, bypass leap inc
|
|
inc hl ; add leap day
|
|
ld c,4 ; reset leap year counter
|
|
d2c3:
|
|
djnz d2c2 ; loop for all years
|
|
|
|
d2c10:
|
|
; Add in days for elapsed months
|
|
ld de,daysmon ; point to start of days per mon tbl
|
|
ld a,(tim$mon) ; get current month
|
|
call bcd2bin ; convert to binary
|
|
dec a ; don't include cur mon
|
|
jr z,d2c20 ; done if Jan
|
|
ld b,a ; save as loop counter
|
|
cp 2 ; Mar = 2
|
|
jr c,d2c11 ; bypass if < Mar (no leap month)
|
|
dec c ; C still has leap year counter
|
|
jr nz,d2c11 ; skip if not leap year
|
|
inc hl ; add in the leap day
|
|
d2c11:
|
|
ld a,(de) ; get days for cur mon
|
|
call addhla ; add to running count
|
|
inc de ; bump to next mon ptr
|
|
djnz d2c11 ; loop for # of months
|
|
|
|
d2c20:
|
|
; Add in days elapsed within month
|
|
; Note that we don't adjust the date to be a zero
|
|
; offset which seems wrong. From what I can tell
|
|
; the CP/M epoch is really 1/0/1978 rather than the
|
|
; 1/1/1978 that the documentation claims. Below seems
|
|
; to work correctly.
|
|
ld a,(tim$day) ; get day
|
|
call bcd2bin ; make binary
|
|
call addhla ; add in days
|
|
ld (@date),hl ; store in SCB
|
|
;call phex16 ; *debug*
|
|
;call crlf ; *debug*
|
|
ret
|
|
|
|
cpm2date:
|
|
; Convert CPM epoch date offset in SCB
|
|
; into YYMMSS values and store result in
|
|
; time buffer at HL.
|
|
|
|
; We start by subtracting years keeping a count
|
|
; of the number of years. Every fourth year is a leap
|
|
; year, so we account for that as we go.
|
|
ld hl,(@date) ; get the count of days since epoch
|
|
dec hl ; because we want 1/1/78 to be offset 0
|
|
ld c,78 ; init the years value
|
|
c2d1:
|
|
ld de,365 ; normal number of days per year
|
|
ld a,c
|
|
ld b,0 ; init leap year flag
|
|
and 03h ; check for leap year
|
|
jr nz,c2d2 ; if not zero, no need to adjust
|
|
inc de ; add a day for leap year
|
|
ld b,1 ; leap year flag for later
|
|
c2d2:
|
|
or a ; clear carry
|
|
sbc hl,de ; subtract
|
|
jr c,c2d3 ; get out if we went too far
|
|
inc c ; add a year to year value
|
|
ld a,c ; to accum
|
|
cp 100 ; century rollover?
|
|
jr nz,c2d1 ; nope, loop
|
|
ld c,0 ; reset for start of century
|
|
jr c2d1 ; loop
|
|
c2d3:
|
|
ld a,c ; years to accum
|
|
call bin2bcd ; convert to bcd
|
|
ld (tim$yr),a ; ... and save it
|
|
;call phex8 ; *debug*
|
|
;
|
|
; Now we use the days per month table to find the
|
|
; month.
|
|
add hl,de ; restore days remaining
|
|
ld c,0 ; init month value (zero indexed)
|
|
c2d4:
|
|
ld a,c ; get month value
|
|
push hl ; save hl (days remaining)
|
|
ld hl,daysmon ; point to start of table
|
|
call addhla ; point to month entry
|
|
ld e,(hl) ; get months day count to E
|
|
ld d,0 ; zero msb, DE is days in month
|
|
pop hl ; recover hl (days remaining)
|
|
ld a,c ; month value to accum
|
|
cp 1 ; leap month? check for Feb
|
|
jr nz,c2d5 ; no, leave alone
|
|
ld a,b ; get leap year flag (set above)
|
|
or a ; leap year?
|
|
jr z,c2d5 ; if not, skip ahead
|
|
inc de ; account for leap year
|
|
c2d5:
|
|
or a ; clear carry
|
|
sbc hl,de ; subtract days for the month
|
|
jr c,c2d6 ; get out if we went too far
|
|
inc c ; next month
|
|
jr c2d4 ; continue
|
|
c2d6:
|
|
inc c ; switch from 0 to 1 offset
|
|
ld a,c ; move to accum
|
|
call bin2bcd ; convert to bcd
|
|
ld (tim$mon),a ; save it
|
|
;call phex8 ; *debug*
|
|
;
|
|
; Leftover days is day value
|
|
add hl,de ; restore days remaining
|
|
ld a,l ; only need lsb
|
|
inc a ; switch from 0 to 1 offset
|
|
call bin2bcd ; convert to bcd
|
|
ld (tim$day),a ; save it
|
|
;call phex8 ; *debug*
|
|
|
|
ret
|
|
|
|
daysmon:
|
|
; days per month
|
|
db 31 ; January
|
|
db 28 ; February (non-leap)
|
|
db 31 ; March
|
|
db 30 ; April
|
|
db 31 ; May
|
|
db 30 ; June
|
|
db 31 ; July
|
|
db 31 ; August
|
|
db 30 ; September
|
|
db 31 ; October
|
|
db 30 ; November
|
|
db 31 ; December
|
|
|
|
; RTC time buffer (all values packed bcd)
|
|
|
|
tim$buf:
|
|
tim$yr db 80h
|
|
tim$mon db 05h
|
|
tim$day db 10h
|
|
tim$hr db 01h
|
|
tim$min db 02h
|
|
tim$sec db 03h
|
|
|
|
open:
|
|
ld c,15
|
|
jp bdos
|
|
|
|
setdma:
|
|
ld c,26
|
|
jp bdos
|
|
|
|
setmulti:
|
|
ld c,44
|
|
jp bdos
|
|
|
|
read:
|
|
ld c,20
|
|
jp bdos
|
|
|
|
clrflg db 0 ; RAM disk cleared flag
|
|
clr$msg db 'RAM Disk Initialized',13,10,13,10,0
|
|
vermis$msg db 7,'*** WARNING: HBIOS/CBIOS Version Mismatch ***',13,10,13,10,0
|
|
|
|
if zpm
|
|
|
|
signon$msg db 13,10,'ZPM3'
|
|
if banked
|
|
db ' [BANKED]'
|
|
endif
|
|
db ' for HBIOS v'
|
|
biosver
|
|
db 13,10,13,10,0
|
|
|
|
ccp$msg db 13,10,'BIOS Err on '
|
|
ccp$msg$drv db 'A'
|
|
db ': No ZCCP.COM file',0
|
|
|
|
|
|
ccp$fcb db 1,'ZCCP ','COM',0,0,0,0
|
|
ds 16
|
|
fcb$nr db 0,0,0
|
|
|
|
else
|
|
|
|
signon$msg db 13,10,'CP/M v3.0'
|
|
if banked
|
|
db ' [BANKED]'
|
|
endif
|
|
db ' for HBIOS v'
|
|
biosver
|
|
db 13,10,13,10,0
|
|
|
|
ccp$msg db 13,10,'BIOS Err on '
|
|
ccp$msg$drv db 'A'
|
|
db ': No CCP.COM file',0
|
|
|
|
|
|
ccp$fcb db 1,'CCP ','COM',0,0,0,0
|
|
ds 16
|
|
fcb$nr db 0,0,0
|
|
|
|
endif
|
|
|
|
@bootdu db 0 ; boot disk unit
|
|
@bootsl db 0 ; boot slice
|
|
hdspv db 2 ; slices per volume for hard disks (must be >= 1)
|
|
drvlst ds 32 ; active drive list used durint drv_init
|
|
drvlstc db 0 ; entry count for active drive list
|
|
|
|
; The following section contains key information and addresses for the
|
|
; RomWBW CBIOS. A pointer to the start of this section is stored with
|
|
; with the CBX data in page zero at $44 (see above).
|
|
|
|
cbx:
|
|
devmapadr dw 0 ; device map address
|
|
drvtbladr dw @dtbl ; drive map address (filled in later)
|
|
dphtbladr dw dph0 ; dpb map address
|
|
cbxsiz equ $ - cbx
|
|
|
|
end
|