mirror of https://github.com/wwarthen/RomWBW.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
577 lines
17 KiB
577 lines
17 KiB
;
|
|
;=======================================================================
|
|
; p-System BIOS for RomWBW HBIOS
|
|
;=======================================================================
|
|
;
|
|
; 3:46 PM 1/13/2023 - WBW - Initial release
|
|
; 5:29 PM 1/15/2023 - WBW - Implemeted extended BIOS functions
|
|
; 10:34 AM 1/16/2023 - WBW - Moved slices into partition
|
|
;
|
|
; TODO:
|
|
;
|
|
; NOTES:
|
|
; - The partition type ID used is the same as the CP/M partition
|
|
; type ID. Might make sense to create a new partition ID which
|
|
; could allow p-System to co-exist with CP/M on a disk image. This
|
|
; would require changes to the RomWBW boot loader as well.
|
|
;
|
|
; - MBR is borrowed from RomWBW CP/M layout, so the partition size
|
|
; is 64 8MB slices. p-System only uses 6 slices. Might be better
|
|
; to create a custom MBR with an appropriate size for p-System
|
|
; partition.
|
|
;
|
|
; - The sysinit routine does a lot of work that just sets up a few
|
|
; variables for later use. This work could be moved into the
|
|
; p-System loader to reduce the size of this BIOS. Since the BIOS
|
|
; is only 768 bytes at this point, I have not bothered with it.
|
|
;
|
|
|
|
#include "../ver.inc"
|
|
|
|
#include "psys.inc"
|
|
|
|
#include "psys_ior.inc"
|
|
|
|
#include "../HBIOS/hbios.inc"
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Local constants
|
|
;-----------------------------------------------------------------------
|
|
|
|
; We need to read and buffer a single sector (MBR) at initialization.
|
|
; It looks like the area just above the loader is the safest place.
|
|
|
|
dskbuf .equ loader_loc + loader_size
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------------
|
|
; BIOS Jump Table
|
|
;-----------------------------------------------------------------------
|
|
|
|
.org bios_loc
|
|
|
|
; Simple BIOS vectors
|
|
jp sysinit ; 0: Initialize machine
|
|
jp syshalt ; 1: Exit UCSD Pascal
|
|
jp coninit ; 2: Console initialize
|
|
jp constat ; 3: Console status
|
|
jp conread ; 4: Console input
|
|
jp conwrit ; 5: Console output
|
|
jp setdisk ; 6: Set disk number
|
|
jp settrak ; 7: Set track number
|
|
jp setsect ; 8: Set sector number
|
|
jp setbufr ; 9: Set buffer address
|
|
jp dskread ; 10: Read sector from disk
|
|
jp dskwrit ; 11: Write sector to disk
|
|
jp dskinit ; 12: Reset disk
|
|
jp dskstrt ; 13: Activate disk
|
|
jp dskstop ; 14: De-activate disk
|
|
|
|
; Extended BIOS vectors
|
|
jp prninit ; 15: Printer initialize
|
|
jp prnstat ; 16: Printer status
|
|
jp prnread ; 17: Printer read
|
|
jp prnwrit ; 18: Printer write
|
|
jp reminit ; 19: Remote initialize
|
|
jp remstat ; 20: Remote status
|
|
jp remread ; 21: Remote read
|
|
jp remwrit ; 22: Remote write
|
|
jp usrinit ; 23: User devices initialize
|
|
jp usrstat ; 24: User devices status
|
|
jp usrread ; 25: User devices read
|
|
jp usrwrit ; 26: User devices write
|
|
jp clkread ; 27: System clock read
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Simple BIOS routines
|
|
;-----------------------------------------------------------------------
|
|
|
|
|
|
sysinit: ; 0: Initialize machine
|
|
; Get critical HBIOS bank ids for use later
|
|
ld b,BF_SYSGET ; HBIOS SysGet function
|
|
ld c,BF_SYSGET_BNKINFO ; BankInfo sub-function
|
|
rst 08 ; do it, D=BIOS, E=USER
|
|
ld (hb_bnks),de ; save bank info
|
|
|
|
; Get boot disk to use for all subsequent disk I/O
|
|
ld b,BF_SYSGET ; HBIOS SysGet function
|
|
ld c,BF_SYSGET_BOOTINFO ; BootInfo sub-function
|
|
rst 08 ; do it, boot disk device unit in
|
|
ld a,d ; boot unit id returned in D
|
|
ld (hb_dev),a ; save for disk I/O
|
|
|
|
; Get the count of serial (CIO) HBIOS devices in system
|
|
ld b,BF_SYSGET ; HBIOS SysGet function
|
|
ld c,BF_SYSGET_CIOCNT ; CIO Count sub-function
|
|
rst 08 ; do it, count in E
|
|
push de ; save it
|
|
|
|
; Get current HBIOS console unit and assign to pSys console
|
|
ld b,BF_SYSPEEK ; HBIOS Peek Function
|
|
ld a,(hb_bios) ; HBIOS bank id
|
|
ld d,a ; ... goes in D
|
|
ld hl,$112 ; offset $112 is current console device
|
|
rst 08 ; call HBIOS, value returned in E
|
|
ld a,e ; move to A
|
|
ld hl,hb_con ; use HL to point to hb_con
|
|
ld (hl),a ; save as console device
|
|
|
|
; Assign additional HBIOS serial devices as pSys remote and printer
|
|
pop bc ; recover CIO count, now in C
|
|
ld a,0 ; assume remote on HB unit 0
|
|
cp (hl) ; conflict?
|
|
jr nz,sysinit1 ; if no conflict, continue
|
|
inc a ; else increment to next unit
|
|
sysinit1:
|
|
cp c ; check for over max serial count
|
|
jr nc,sysinit3 ; if exceeded, we are done
|
|
ld (hb_rem),a ; assign remote device
|
|
inc a ; bump to next dev for printer
|
|
cp (hl) ; conflict?
|
|
jr nz,sysinit2 ; if no conflict, continue
|
|
inc a ; else increment to next unit
|
|
sysinit2:
|
|
cp c ; check for over max serial count
|
|
jr nc,sysinit3 ; if exceeded, we are done
|
|
ld (hb_prn),a ; assign printer device
|
|
sysinit3:
|
|
|
|
; Announce BIOS
|
|
ld hl,str_banner ; load version banner
|
|
call prtstr ; and display it
|
|
;call conread ; wait for user
|
|
|
|
; The p-System slices live within a disk partition. So, now we
|
|
; read the MBR, look for our partition ID, extract the
|
|
; corresponding LBA offset and save it for subsequent disk I/O.
|
|
|
|
; Read MBR. The MBR lives in the first sector of the hard
|
|
; disk. At this point paroff, curdisk, curtrak, and cursect
|
|
; are all zero. So, we just set the disk buffer and make a
|
|
; disk I/O call which results in reading the first (MBR)
|
|
; sector.
|
|
ld bc,dskbuf ; load disk buf adr
|
|
ld (curbufr),bc ; save it
|
|
call dskread ; read first sector of phy disk
|
|
jp nz,parterr ; abort on error
|
|
|
|
; Check signature
|
|
ld hl,(dskbuf+$1FE) ; get signature
|
|
ld a,l ; first byte
|
|
cp $55 ; should be $55
|
|
jp nz,parterr ; if not, no part table
|
|
ld a,h ; second byte
|
|
cp $AA ; should be $AA
|
|
jp nz,parterr ; if not, no part table
|
|
|
|
; Search part table for entry (type 0x2E)
|
|
ld b,4 ; four entries in part table
|
|
ld hl,dskbuf+$1BE+4 ; offset of first part type
|
|
sysinit4:
|
|
ld a,(hl) ; get part type
|
|
cp $2E ; CP/M partition?
|
|
jr z,sysinit5 ; cool, grab the LBA offset
|
|
ld de,16 ; part table entry size
|
|
add hl,de ; bump to next part type
|
|
djnz sysinit4 ; loop thru table
|
|
jp parterr ; too bad, no CP/M partition
|
|
sysinit5:
|
|
; Capture the starting LBA of the partition we found
|
|
ld de,4 ; LBA is 4 bytes after part type
|
|
add hl,de ; point to it
|
|
ld de,paroff ; loc to store lba offset
|
|
ld bc,4 ; 4 bytes (32 bits)
|
|
ldir ; copy it
|
|
|
|
sysinit6:
|
|
|
|
; Vector sysinit is being called twice during startup. Once
|
|
; from the bootstrap and then from the interpreter. So, we
|
|
; remap the vector here to avoid doing the above stuff
|
|
; multiple times.
|
|
ld hl,sysinitz ; re-vector to sysinitz
|
|
ld (bios_loc+1),hl ; update the jump table
|
|
|
|
sysinitz:
|
|
ret ; done
|
|
xor a ; signal success
|
|
|
|
syshalt: ; 1: Exit UCSD Pascal
|
|
; The syshalt vector does not seem be to invoked when
|
|
; selecting the Halt option from the p-System menu.
|
|
; I have no idea why.
|
|
ld b,BF_SYSRESET ; HBIOS reset function
|
|
ld c,BF_SYSRES_WARM ; warm reset is fine
|
|
rst 08 ; do it
|
|
|
|
; We should never get here.
|
|
di ; interrupts off
|
|
halt ; ... and die
|
|
|
|
|
|
coninit: ; 2: Console initialize
|
|
ld a,(hb_con) ; initialize console unit
|
|
jp serinit ; do it
|
|
|
|
constat: ; 3: Console status
|
|
ld a,(hb_con) ; status of console unit
|
|
jp serstat ; do it
|
|
|
|
conread: ; 4: Console input
|
|
ld a,(hb_con) ; read from console unit
|
|
jp serread ; do it
|
|
|
|
conwrit: ; 5: Console output
|
|
ld a,c
|
|
cp 27 ; escape?
|
|
ld a,(hb_con) ; write to console unit
|
|
jp nz,serwrit ; if not, handle normally
|
|
call serwrit ; else, send escape
|
|
ld c,'[' ; ... followed by '[' for ANSI
|
|
ld a,(hb_con) ; write to console unit
|
|
jp serwrit ; do it
|
|
|
|
setdisk: ; 6: Set disk number
|
|
ld a,c ; disk number to A
|
|
ld (curdisk),a ; save for later
|
|
|
|
; Each p-System disk lives in a slice. Additionally,
|
|
; the start of the slices is determined by the hard
|
|
; disk partition table. To avoid computing the p-System
|
|
; disk offset on every I/O call, below we pre-compute
|
|
; the physical HBIOS disk LBA offset for the slice of the
|
|
; p-System disk being selected here.
|
|
ld hl,(paroff) ; initialize DE:HL
|
|
ld de,(paroff+2) ; ... to start of partition
|
|
or a ; use A as loop ctr, check for zero
|
|
jr z,setdisk2 ; if 0, no slice offset needed
|
|
setdisk1:
|
|
ld bc,(sps) ; get low word of sps
|
|
add hl,bc ; add low words
|
|
ex de,hl ; swap high word into HL
|
|
ld bc,(sps+2) ; get high word of sps
|
|
adc hl,bc ; add high words (w/ carry)
|
|
ex de,hl ; swap back to get DE:HL
|
|
dec a ; dec loop ctr
|
|
jr nz,setdisk1 ; rinse and repeat
|
|
setdisk2:
|
|
ld (curoff),hl ; save low word
|
|
ld (curoff+2),de ; save high word
|
|
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
settrak: ; 7: Set track number
|
|
ld a,c ; track number to A
|
|
ld (curtrak),a ; save for later
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
setsect: ; 8: Set sector number
|
|
ld a,c ; sector number to A
|
|
dec a ; from 1 indexed to 0 indexed
|
|
ld (cursect),a ; save for later
|
|
xor a ; signal success
|
|
ret
|
|
|
|
setbufr: ; 9: Set buffer address
|
|
ld (curbufr),bc ; save buf adr for later
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
dskread: ; 10: Read sector from disk
|
|
call chkdisk
|
|
ret nz
|
|
|
|
call seek
|
|
ret nz
|
|
|
|
ld b,BF_DIOREAD ; HBIOS disk read function
|
|
ld a,(hb_dev) ; HBIOS disk unit
|
|
ld c,a ; ... goes in C
|
|
ld a,(HB_CURBNK) ; get current memory bank
|
|
ld d,a ; use as target bank for transfer
|
|
ld e,1 ; read 1 sector
|
|
ld hl,(curbufr) ; disk read buffer adr
|
|
rst 08 ; do it
|
|
ret z ; return if good read
|
|
ld a,ior_badblk ; else i/o error
|
|
ret ; done
|
|
|
|
dskwrit: ; 11: Write sector to disk
|
|
call chkdisk
|
|
ret nz
|
|
|
|
call seek
|
|
ret nz
|
|
|
|
ld b,BF_DIOWRITE ; HBIOS disk read function
|
|
ld a,(hb_dev) ; HBIOS disk unit
|
|
ld c,a ; ... goes in C
|
|
ld a,(HB_CURBNK) ; get current memory bank
|
|
ld d,a ; use as target bank for transfer
|
|
ld e,1 ; read 1 sector
|
|
ld hl,(curbufr) ; disk read buffer adr
|
|
rst 08 ; do it
|
|
ret z ; return if good read
|
|
ld a,ior_badblk ; else i/o error
|
|
ret ; done
|
|
|
|
dskinit: ; 12: Reset disk
|
|
call chkdisk
|
|
ret nz
|
|
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
dskstrt: ; 13: Activate disk
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
dskstop: ; 14: De-activate disk
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Extended BIOS routines
|
|
;-----------------------------------------------------------------------
|
|
|
|
prninit: ; 15: Printer initialize
|
|
ld a,(hb_prn) ; initialize printer unit
|
|
jp serinit ; do it
|
|
|
|
prnstat: ; 16: Printer status
|
|
ld a,(hb_prn) ; status of printer unit
|
|
jp serstat ; do it
|
|
|
|
prnread: ; 17: Printer read
|
|
ld a,(hb_prn) ; read from printer unit
|
|
jp serread ; do it
|
|
|
|
prnwrit: ; 18: Printer write
|
|
ld a,(hb_prn) ; write to printer unit
|
|
jp serwrit ; do it
|
|
|
|
reminit: ; 19: Remote initialize
|
|
ld a,(hb_rem) ; initialize remote unit
|
|
jp serinit ; do it
|
|
|
|
remstat: ; 20: Remote status
|
|
ld a,(hb_rem) ; status of remote unit
|
|
jp serstat ; do it
|
|
|
|
remread: ; 21: Remote read
|
|
ld a,(hb_rem) ; read from remote unit
|
|
jp serread ; do it
|
|
|
|
remwrit: ; 22: Remote write
|
|
ld a,(hb_rem) ; write to remote unit
|
|
jp serwrit ; do it
|
|
|
|
usrinit: ; 23: User devices initialize
|
|
ld a,9 ; offline status
|
|
ret
|
|
|
|
usrstat: ; 24: User devices status
|
|
pop hl ; return address
|
|
pop de ; discard input/output toggle
|
|
pop de ; discard ptr to status rec
|
|
pop de ; discard device number
|
|
ld a,9 ; offline status
|
|
jp (hl) ; return
|
|
|
|
usrread: ; 25: User devices read
|
|
usrwrit: ; 26: User devices write
|
|
pop hl ; return address
|
|
pop de ; extra parameter 2
|
|
pop de ; extra parameter 1
|
|
pop de ; pointer to buffer
|
|
pop de ; device number
|
|
pop de ; extra parameter 5
|
|
ld a,9 ; offline status
|
|
jp (hl) ; return
|
|
|
|
clkread: ; 27: System clock read
|
|
ld b,BF_SYSGET ; HBIOS SysGet function
|
|
ld c,BF_SYSGET_TIMER ; Timer sub-function
|
|
rst 08 ; do it, ticks ret in DE:HL
|
|
ex de,hl ; swap for pSys
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Support routines
|
|
;-----------------------------------------------------------------------
|
|
|
|
serinit:
|
|
; Initialize HBIOS serial port identified in reg A
|
|
cp $FF ; do we have desired port?
|
|
jr z,nodev ; handle it if so
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
serstat:
|
|
; Check status of HBIOS serial port identified in reg A
|
|
cp $FF ; do we have desired port?
|
|
jr z,nodev ; handle it if so
|
|
ld b,BF_CIOIST ; serial port status function
|
|
ld c,a ; HBIOS serial port
|
|
rst 08 ; call HBIOS
|
|
ld c,0 ; assume no chars pendin
|
|
jr z,serstat1 ; if zero, no chars waiting
|
|
ld c,$FF ; signal char(s) pending
|
|
serstat1:
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
serread:
|
|
; Read one byte from HBIOS serial port identified in reg A
|
|
cp $FF ; do we have desired port?
|
|
jr z,nodev ; handle it if so
|
|
ld b,BF_CIOIN ; serial port read function
|
|
ld c,a ; HBIOS serial port
|
|
rst 08 ; call HBIOS
|
|
ld c,e ; char to C
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
serwrit:
|
|
; Write one byte to HBIOS serial port identified in reg A
|
|
cp $FF ; do we have desired port?
|
|
jr z,nodev ; handle it if so
|
|
ld e,c ; char to write to E
|
|
ld b,BF_CIOOUT ; serial port write function
|
|
ld c,a ; HBIOS serial port
|
|
rst 08 ; call HBIOS
|
|
xor a ; signal success
|
|
ret ; done
|
|
|
|
nodev:
|
|
ld a,9 ; signal volume offline
|
|
ret ; and done
|
|
|
|
chkdisk:
|
|
; Validate that curdisk is <= max supported
|
|
ld a,(curdisk) ; get current disk
|
|
cp disks ; compare to disk count
|
|
jr nc,chkdisk1 ; if too high, go to err
|
|
xor a ; signal success
|
|
ret ; done
|
|
chkdisk1:
|
|
ld a,ior_novol ; signal not online
|
|
or a ; set flags
|
|
ret ; done
|
|
|
|
seek:
|
|
; We use LBA addressing for disk access. So, we need to
|
|
; translate the track/sector value from p-System into an
|
|
; lba offset. Since we are using 16 sectors per track, we
|
|
; can cheat (avoid multiplication) by using the low 4 bits
|
|
; for sector and the high bits for track which allows us to
|
|
; just "or" the values together. We are only using word values
|
|
; here since that will handle up to a 32MB p-System file system
|
|
; (slice) which is more than enough.
|
|
|
|
ld a,(curtrak) ; cur track in accum
|
|
ld l,a ; move to low byte of HL
|
|
ld h,0 ; zero out high byte of HL
|
|
add hl,hl ; * 2
|
|
add hl,hl ; * 4
|
|
add hl,hl ; * 8
|
|
add hl,hl ; * 16 (sectors per track)
|
|
ld a,(cursect) ; cur sec to accum
|
|
or l ; combine with low byte of HL
|
|
ld l,a ; back to low byte of HL
|
|
|
|
; HL now has LBA offset of desired sector. Next
|
|
; we need to add in the offset of the current disk.
|
|
; At this point, we need to start using dword values
|
|
; using DE:HL to accommodate large disk drives.
|
|
ld de,0 ; extend LBA to DE:HL
|
|
ld bc,(curoff) ; get low word of offset
|
|
add hl,bc ; add low words together
|
|
ex de,hl ; swap high word of LBA into HL
|
|
ld bc,(curoff+2) ; get high word of offset
|
|
adc hl,bc ; add high words together (w/ carry)
|
|
ex de,hl ; swap back to get DE:HL
|
|
|
|
; Now we have final LBA in DE:HL. We just set the
|
|
; LBA flag bit and do the disk seek.
|
|
set 7,d ; high order bit designates LBA I/O
|
|
ld b,BF_DIOSEEK ; HBIOS seek function
|
|
ld a,(hb_dev) ; HBIOS disk unit
|
|
ld c,a ; ... goes in C
|
|
rst 08 ; do it
|
|
ret z ; if no error, done
|
|
ld a,ior_badblk ; signal I/O error
|
|
ret ; done
|
|
|
|
prtstr:
|
|
; Print a null terminated string on the p-System console
|
|
ld a,(hl) ; get next char
|
|
or a ; set flags
|
|
ret z ; done if null
|
|
push hl ; save buffer pointer
|
|
ld c,a ; char to C
|
|
call conwrit ; write it out to pSys console
|
|
pop hl ; recover buffer pointer
|
|
inc hl ; increment to next char
|
|
jr prtstr ; loop as needed
|
|
|
|
parterr:
|
|
ld hl,str_parterr ; partition error string
|
|
call prtstr ; display it
|
|
jp syshalt ; back to boot loader
|
|
|
|
panic:
|
|
; Hard stop
|
|
di ; no interrupts
|
|
halt ; ... and halt
|
|
|
|
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Local storage
|
|
;-----------------------------------------------------------------------
|
|
|
|
hb_bnks:
|
|
hb_usr .db 0 ; HBIOS User bank id
|
|
hb_bios .db 0 ; HBIOS BIOS bank id
|
|
|
|
hb_dev .db 0 ; HBIOS device for pSys disk
|
|
hb_con .db $FF ; HBIOS device for pSys console unit
|
|
hb_rem .db $FF ; HBIOS device for pSys remote unit
|
|
hb_prn .db $FF ; HBIOS device for pSys printer unit
|
|
|
|
curdisk .db 0 ; Current pSys disk number
|
|
curtrak .db 0 ; Current pSys track number
|
|
cursect .db 0 ; Current pSys sector number
|
|
curbufr .dw 0 ; Current pSys disk buffer address
|
|
curoff .dw 0,0 ; Current sector offset (dword LBA)
|
|
|
|
paroff .dw 0,0 ; Partition offset (dword LBA)
|
|
sps .dw 16384,0 ; Sectors per slice (8MB / 512) = 16384
|
|
|
|
str_banner .db 13,10,13,10,"RomWBW p-System Extended BIOS v"
|
|
.db BIOSVER,0
|
|
str_parterr .db 13,10,"*** Disk partition table error!",0
|
|
|
|
|
|
|
|
#if ($ >= bios_end)
|
|
.echo "*** ERROR: Out of space in pSystem BIOS!!!\n"
|
|
!!! ; force an assembly error
|
|
#endif
|
|
|
|
slack .equ bios_end - $
|
|
.echo "pSystem BIOS space remaining: "
|
|
.echo slack
|
|
.echo " bytes.\n"
|
|
|
|
.fill bios_end - $
|
|
|
|
.end
|
|
|