mirror of
https://github.com/wwarthen/RomWBW.git
synced 2026-02-06 14:11:48 -06:00
- The baud rate change functionality in ROM Loader was refactored to reduce it's size and be consistent with the approach used in the MODE command.
2579 lines
66 KiB
NASM
2579 lines
66 KiB
NASM
;
|
|
;=======================================================================
|
|
; RomWBW Loader
|
|
;=======================================================================
|
|
;
|
|
; The loader code is invoked immediately after HBIOS completes system
|
|
; initialization. it is responsible for loading a runnable image
|
|
; (operating system, etc.) into memory and transferring control to that
|
|
; image. The image may come from ROM (romboot), RAM (appboot/imgboot)
|
|
; or from disk (disk boot).
|
|
;
|
|
; In the case of a ROM boot, the selected executable image is copied
|
|
; from ROM into the default RAM and then control is passed to the
|
|
; starting address in RAM. In the case of an appboot or imgboot
|
|
; startup (see hbios.asm) the source of the image may be RAM.
|
|
;
|
|
; In the case of a disk boot, sector 2 (the third sector) of the disk
|
|
; device will be read -- this is the boot info sector and is expected
|
|
; to have the format defined at bl_infosec below. the last three words
|
|
; of data in this sector determine the final destination starting and
|
|
; ending address for the disk load operation as well as the entry point
|
|
; to transfer control to. The actual image to be loaded *must* be on
|
|
; the disk in the sectors immediately following the boot info sector.
|
|
; This means the image to be loaded must begin in sector 3 (the fourth
|
|
; sector) and occupy sectors contiguously after that.
|
|
;
|
|
; The code below relocates itself at startup to the start of common RAM
|
|
; at $8000. This means that the code, data, and stack will all stay
|
|
; within $8000-$8FFF. Since all code images like to be loaded either
|
|
; high or low (never in the middle), the $8000-$8FFF location tends to
|
|
; avoid the problem where the code is overlaid during the loading of
|
|
; the desired executable image.
|
|
;
|
|
#include "std.asm" ; standard RomWBW constants
|
|
;
|
|
#ifndef BOOT_DEFAULT
|
|
#define BOOT_DEFAULT "H"
|
|
#endif
|
|
;
|
|
bel .equ 7 ; ASCII bell
|
|
bs .equ 8 ; ASCII backspace
|
|
lf .equ 10 ; ASCII linefeed
|
|
cr .equ 13 ; ASCII carriage return
|
|
del .equ 127 ; ASCII del/rubout
|
|
;
|
|
cmdbuf .equ $80 ; cmd buf is in second half of page zero
|
|
cmdmax .equ 60 ; max cmd len (arbitrary), must be < bufsiz
|
|
bufsiz .equ $80 ; size of cmd buf
|
|
;
|
|
hbx_int .equ $FF60 ; IM1 vector target for RomWBW HBIOS proxy
|
|
;
|
|
bid_cur .equ -1 ; used below to indicate current bank
|
|
;
|
|
.org 0 ; we expect to be loaded at $0000
|
|
;
|
|
;=======================================================================
|
|
; Normal page zero setup, ret/reti/retn as appropriate
|
|
;=======================================================================
|
|
;
|
|
jp $100 ; rst 0: jump to boot code
|
|
.fill ($08 - $)
|
|
#if (BIOS == BIOS_WBW)
|
|
jp HB_INVOKE ; rst 8: invoke HBIOS function
|
|
#else
|
|
jp $FFFD ; rst 8: invoke UBIOS function
|
|
#endif
|
|
.fill ($10 - $)
|
|
ret ; rst 10
|
|
.fill ($18 - $)
|
|
ret ; rst 18
|
|
.fill ($20 - $)
|
|
ret ; rst 20
|
|
.fill ($28 - $)
|
|
ret ; rst 28
|
|
.fill ($30 - $)
|
|
ret ; rst 30
|
|
.fill ($38 - $)
|
|
#if (BIOS == BIOS_WBW)
|
|
#if (INTMODE == 1)
|
|
call hbx_int ; handle im1 interrupts
|
|
.db $10 << 2 ; use special vector #16
|
|
#else
|
|
ret ; return w/ ints left disabled
|
|
#endif
|
|
#else
|
|
ret ; return w/ ints disabled
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
.fill ($40 - $),$FF
|
|
; After initial bootup, it is conventional for a jp 0 to
|
|
; cause a warm start of the system. If there is no OS running
|
|
; then this bit of code will suffice. After bootup, the
|
|
; jp instruction at $0 is modified to point here.
|
|
pop hl ; save PC in case needed for ...
|
|
ld bc,$F003 ; HBIOS user reset function
|
|
call HB_INVOKE ; do it
|
|
ld bc,$F001 ; HBIOS warm start function
|
|
call HB_INVOKE ; do it
|
|
#endif
|
|
;
|
|
.fill ($66 - $)
|
|
retn ; nmi
|
|
;
|
|
.fill ($100 - $) ; pad remainder of page zero
|
|
;
|
|
;=======================================================================
|
|
; Startup and loader initialization
|
|
;=======================================================================
|
|
;
|
|
; Note: at startup, we should not assume which bank we are operating in.
|
|
;
|
|
; Relocate to start of common ram at $8000
|
|
ld hl,0
|
|
ld de,$8000
|
|
ld bc,LDR_SIZ
|
|
ldir
|
|
;
|
|
jp start
|
|
;
|
|
.ORG $8000 + $
|
|
;
|
|
start:
|
|
ld sp,bl_stack ; setup private stack
|
|
call delay_init ; init delay functions
|
|
;
|
|
; Disable interrupts if IM1 is active because we are switching to page
|
|
; zero in user bank and it has not been prepared with IM1 vector yet.
|
|
;
|
|
#if (INTMODE == 1)
|
|
di
|
|
#endif
|
|
;
|
|
; Switch to user RAM bank and establish boot mode
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
; Get the boot mode
|
|
ld b,BF_SYSPEEK ; HBIOS func: PEEK
|
|
ld d,BID_BIOS ; BIOS bank
|
|
ld hl,HCB_LOC + HCB_BOOTMODE ; boot mode byte
|
|
rst 08
|
|
ld a,e ; put in A
|
|
ld (bootmode),a ; save it
|
|
;
|
|
ld b,BF_SYSSETBNK ; HBIOS func: set bank
|
|
ld c,BID_USR ; select user bank
|
|
rst 08 ; do it
|
|
ld a,c ; previous bank to A
|
|
ld (bid_ldr),a ; save previous bank for later
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
ld bc,$01FB ; UNA func: set bank
|
|
ld de,BID_USR ; select user bank
|
|
rst 08 ; do it
|
|
ld (bid_ldr),de ; save previous bank for later
|
|
;
|
|
ld a,BM_ROMBOOT ; assume ROM boot
|
|
bit 7,d ; starting from ROM?
|
|
jr z,start1 ; if so, skip ahead
|
|
ld a,BM_APPBOOT ; else this is APP boot
|
|
start1:
|
|
ld (bootmode),a ; save it
|
|
#endif
|
|
;
|
|
; For app mode startup, use alternate table
|
|
ld hl,ra_tbl ; assume ROM application table
|
|
ld a,(bootmode) ; get boot mode
|
|
cp BM_ROMBOOT ; ROM boot?
|
|
jr z,start2 ; if so, ra_tbl OK, skip ahead
|
|
ld hl,ra_tbl_app ; switch to RAM application table
|
|
start2:
|
|
ld (ra_tbl_loc),hl ; and overlay pointer
|
|
;
|
|
; Copy original page zero into user page zero
|
|
;
|
|
ld hl,$8000 ; page zero was copied here
|
|
ld de,0 ; put it in user page zero
|
|
ld bc,$100 ; full page
|
|
ldir ; do it
|
|
ld hl,$0040 ; adr of user reset code
|
|
ld (1),hl ; save at $0000
|
|
;
|
|
; Page zero in user bank is ready for interrupts now.
|
|
;
|
|
#if (INTMODE == 1)
|
|
ei
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
; Get the current console unit
|
|
ld b,BF_SYSPEEK ; HBIOS func: PEEK
|
|
ld d,BID_BIOS ; BIOS bank
|
|
ld hl,HCB_LOC + HCB_CONDEV ; console unit num in HCB
|
|
rst 08 ; do it
|
|
ld a,e ; put in A
|
|
ld (curcon),a ; save it
|
|
;
|
|
; Get character unit count
|
|
ld b,BF_SYSGET ; HBIOS func: SYS GET
|
|
ld c,BF_SYSGET_CIOCNT ; HBIOS subfunc: CIO unit count
|
|
rst 08 ; E := unit count
|
|
ld a,e ; put in A
|
|
ld (ciocnt),a ; save it
|
|
;
|
|
; Check for DSKY and set flag
|
|
ld b,BF_SYSGET ; HBIOS func: get
|
|
ld c,BF_SYSGET_DSKYCNT ; get DSKY count
|
|
rst 08 ; do it
|
|
ld a,e ; put in A
|
|
ld (dskyact),a ; save it
|
|
#endif
|
|
|
|
;
|
|
;=======================================================================
|
|
; Loader prompt
|
|
;=======================================================================
|
|
;
|
|
call nl2 ; formatting
|
|
ld hl,str_banner ; display boot banner
|
|
call pstr ; do it
|
|
ld a,(bootmode) ; get app boot flag
|
|
cp BM_APPBOOT ; APP boot?
|
|
ld hl,str_appboot ; signal application boot mode
|
|
call z,pstr ; print if APP boot
|
|
call clrbuf ; zero fill the cmd buffer
|
|
;
|
|
#if ((BIOS == BIOS_WBW) & FPSW_ENABLE)
|
|
;
|
|
ld b,BF_SYSGET ; HBIOS SysGet
|
|
ld c,BF_SYSGET_PANEL ; ... Panel swiches value
|
|
rst 08 ; do it
|
|
jr nz,nofp ; no switches, skip over
|
|
ld a,l ; put value in A
|
|
ld (switches),a ; save it
|
|
;
|
|
call nl ; formatting
|
|
ld hl,str_switches ; tag
|
|
call pstr ; display
|
|
ld a,(switches) ; get switches value
|
|
call prthexbyte ; display
|
|
;
|
|
ld a,(switches) ; get switches value
|
|
and SW_AUTO ; auto boot?
|
|
call nz,runfp ; process front panel
|
|
;
|
|
nofp:
|
|
; fall thru
|
|
;
|
|
#endif
|
|
;
|
|
#if (BOOT_TIMEOUT != -1)
|
|
; Initialize auto command timeout downcounter
|
|
or $FF ; auto cmd active value
|
|
ld (acmd_act),a ; set flag
|
|
ld bc,BOOT_TIMEOUT * 100 ; hundredths of seconds
|
|
ld (acmd_to),bc ; save auto cmd timeout
|
|
;
|
|
; If timeout is zero, boot auto command immediately
|
|
ld a,b ; check for
|
|
or c ; ... zero
|
|
jr nz,prompt ; not zero, prompt w/ timeout
|
|
call nl2 ; formatting
|
|
ld hl,str_autoboot ; auto command prefix
|
|
call pstr ; show it
|
|
call autocmd ; handle w/o prompt
|
|
jr reprompt ; restart w/ autocmd disable
|
|
#endif
|
|
;
|
|
prompt:
|
|
ld hl,reprompt ; adr of prompt restart routine
|
|
push hl ; put it on stack
|
|
call nl2 ; formatting
|
|
ld hl,str_prompt ; display boot prompt "Boot [H=Help]:"
|
|
call pstr ; do it
|
|
call clrbuf ; zero fill the cmd buffer
|
|
;
|
|
;ld hl,msg_sel ; boot select msg
|
|
;call dsky_show ; show on DSKY
|
|
ld c,DSKY_MSG_LDR_SEL ; boot select msg
|
|
call dsky_msg ; show on DSKY
|
|
#if (DSKYENABLE)
|
|
call dsky_highlightallkeys
|
|
call dsky_beep
|
|
call dsky_l2on
|
|
#endif
|
|
;
|
|
; purge any garbage on the line
|
|
call delay ; wait for prompt to be sent
|
|
ld b,0 ; failsafe max iterations
|
|
purge:
|
|
call cst ; anything there?
|
|
jr z,wtkey ; if not, move on
|
|
call cin ; read and discard
|
|
djnz purge ; and loop till no more
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
#if (AUTOCON)
|
|
or $ff ; initial value
|
|
ld (conpend),a ; ... for conpoll routine
|
|
#endif
|
|
#endif
|
|
;
|
|
wtkey:
|
|
; wait for a key or timeout
|
|
call cst ; check for keyboard key
|
|
jr nz,concmd ; if pending, do console command
|
|
; NOTE Above is like a CALL, with a RET to reprompt: (manually pushed)
|
|
;
|
|
#if (DSKYENABLE)
|
|
call dsky_stat ; check DSKY for keypress
|
|
jp nz,dskycmd ; if pending, do DSKY command
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
#if (AUTOCON)
|
|
call conpoll ; poll for console takeover
|
|
jp nz,docon ; if requested, takeover
|
|
#endif
|
|
#endif
|
|
;
|
|
#if (BOOT_TIMEOUT != -1)
|
|
; check for timeout and handle auto boot here
|
|
ld a,(acmd_act) ; get auto cmd active flag
|
|
or a ; set flags
|
|
jr z,wtkey ; if not active, just loop
|
|
ld bc,(acmd_to) ; load timeout value
|
|
ld a,b ; test for
|
|
or c ; ... zero
|
|
jr z,autocmd ; if so, handle it
|
|
dec bc ; decrement
|
|
ld (acmd_to),bc ; resave it
|
|
ld de,625 ; 16us * 625 = 10ms
|
|
call vdelay ; 10ms delay
|
|
#endif
|
|
;
|
|
jr wtkey ; loop
|
|
;
|
|
reprompt:
|
|
xor a ; zero accum
|
|
ld (acmd_act),a ; set auto cmd inactive
|
|
jr prompt ; back to loader prompt
|
|
;
|
|
clrbuf:
|
|
ld hl,cmdbuf
|
|
ld b,bufsiz
|
|
xor a
|
|
clrbuf1:
|
|
ld (hl),a
|
|
djnz clrbuf1
|
|
ret
|
|
;
|
|
;=======================================================================
|
|
; Poll character units for console takeover request
|
|
;=======================================================================
|
|
;
|
|
; Poll all character units in system for a console takeover request.
|
|
; A takeover request consists of pressing the <space> twice in a row.
|
|
; at the character unit that wants to be the console. Return with ZF
|
|
; set if a console takeover was requested. If so, the requested console
|
|
; unit will be recorded in (newcon).
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
#if (AUTOCON)
|
|
;
|
|
conpoll:
|
|
; save active console unit
|
|
ld a,(curcon)
|
|
ld e,a ; save in E
|
|
;
|
|
; loop through all char ports
|
|
ld a,(ciocnt) ; count of char units
|
|
ld b,a ; use for loop counter
|
|
ld c,0 ; init unit num
|
|
;
|
|
conpoll1:
|
|
ld a,c ; next char unit to test
|
|
cp e ; is this the active console?
|
|
jr z,conpoll2 ; if so, don't test, move on
|
|
ld (curcon),a ; make it current port
|
|
call cst ; char waiting?
|
|
jr z,conpoll2 ; if no char, move on
|
|
call cin ; get char
|
|
cp ' ' ; space char?
|
|
jr nz,conpoll2 ; if not, move on
|
|
;
|
|
; a <space> char was typed. check to see if we just saw a
|
|
; <space> from this same unit.
|
|
ld a,(conpend) ; pending con unit to A
|
|
cp c ; compare to active unit
|
|
jr z,conpoll3 ; if =, second <space>, take con
|
|
ld a,c ; if not, unit to A
|
|
ld (conpend),a ; and update pending console
|
|
;
|
|
conpoll2:
|
|
inc c ; next char unit
|
|
djnz conpoll1 ; loop till done
|
|
xor a ; ret w/ Z for no takeover
|
|
jr conpoll4 ; all done, no takeover
|
|
;
|
|
conpoll3:
|
|
; record a new console request
|
|
ld a,(curcon) ; record the unit
|
|
ld (newcon),a ; ... as new console
|
|
or $ff ; ret w/ NZ for new con req
|
|
;
|
|
conpoll4:
|
|
; restore active console and exit
|
|
ld a,e ; restore active
|
|
ld (curcon),a ; ... console
|
|
ret ; done, NZ if new con request
|
|
;
|
|
#endif
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Process a command line from buffer
|
|
;=======================================================================
|
|
;
|
|
concmd:
|
|
;
|
|
#if (DSKYENABLE)
|
|
call dsky_highlightkeysoff
|
|
call dsky_l2off
|
|
#endif
|
|
;
|
|
; Get a command line from console and handle it
|
|
call rdln ; get a line from the user
|
|
ld de,cmdbuf ; point to buffer
|
|
call skipws ; skip whitespace
|
|
or a ; set flags to check for null
|
|
jr nz,runcmd ; got a cmd, process it
|
|
; if no cmd entered, fall thru to process default cmd
|
|
;
|
|
autocmd:
|
|
; Copy autocmd string to buffer and process it
|
|
ld hl,acmd ; auto cmd string
|
|
call pstr ; display it
|
|
ld hl,acmd ; auto cmd string
|
|
ld de,cmdbuf ; cmd buffer adr
|
|
ld bc,acmd_len ; auto cmd length
|
|
ldir ; copy to command line buffer
|
|
;
|
|
runcmd:
|
|
; Process command line
|
|
;
|
|
ld de,cmdbuf ; point to start of buf
|
|
call skipws ; skip whitespace
|
|
or a ; check for null terminator
|
|
ret z ; if empty line, just bail out
|
|
ld a,(de) ; get character
|
|
call upcase ; make upper case
|
|
;
|
|
; Attempt built-in commands
|
|
cp 'H' ; H = display help
|
|
jp z,help ; if so, do it
|
|
cp '?' ; '?' alias for help
|
|
jp z,help ; if so, do it
|
|
cp 'L' ; L = List ROM applications
|
|
jp z,applst ; if so, do it
|
|
cp 'D' ; D = device inventory
|
|
jp z,devlst ; if so, do it
|
|
cp 'R' ; R = reboot system
|
|
jp z,reboot ; if so, do it
|
|
#if (BIOS == BIOS_WBW)
|
|
cp 'I' ; C = set console interface
|
|
jp z,setcon ; if so, do it
|
|
cp 'V' ; V = diagnostic verbosity
|
|
jp z,setdl ; is so, do it
|
|
#endif
|
|
;
|
|
; Attempt ROM application launch
|
|
ld ix,(ra_tbl_loc) ; point to start of ROM app tbl
|
|
ld c,a ; save command in C
|
|
runcmd1:
|
|
ld a,(ix+ra_conkey) ; get match char
|
|
and ~$80 ; clear "hidden entry" bit
|
|
cp c ; compare
|
|
jp z,romload ; if match, load it
|
|
ld de,ra_entsiz ; table entry size
|
|
add ix,de ; bump IX to next entry
|
|
ld a,(ix) ; check for end
|
|
or (ix+1) ; ... of table
|
|
jr nz,runcmd1 ; loop till done
|
|
;
|
|
; Attempt disk boot
|
|
ld de,cmdbuf ; start of buffer
|
|
call skipws ; skip whitespace
|
|
call isnum ; do we have a number?
|
|
jp nz,err_invcmd ; invalid format if empty
|
|
call getnum ; parse a number
|
|
jp c,err_invcmd ; handle overflow error
|
|
ld (bootunit),a ; save boot unit
|
|
xor a ; zero accum
|
|
ld (bootslice),a ; save default slice
|
|
call skipws ; skip possible whitespace
|
|
ld a,(de) ; get separator char
|
|
or a ; test for terminator
|
|
jp z,diskboot ; if so, boot the disk unit
|
|
cp '.' ; otherwise, is '.'?
|
|
jr z,runcmd2 ; yes, handle slice spec
|
|
cp ':' ; or ':'?
|
|
jr z,runcmd2 ; alt sep for slice spec
|
|
jp err_invcmd ; if not, format error
|
|
runcmd2:
|
|
inc de ; bump past separator
|
|
call skipws ; skip possible whitespace
|
|
call isnum ; do we have a number?
|
|
jp nz,err_invcmd ; if not, format error
|
|
call getnum ; get number
|
|
jp c,err_invcmd ; handle overflow error
|
|
ld (bootslice),a ; save boot slice
|
|
jp diskboot ; boot the disk unit/slice
|
|
;
|
|
#if ((BIOS == BIOS_WBW) & FPSW_ENABLE)
|
|
;
|
|
;=======================================================================
|
|
; Process Front Panel switches
|
|
;=======================================================================
|
|
;
|
|
runfp:
|
|
ld a,(switches) ; get switches value
|
|
and SW_DISK ; disk boot?
|
|
jr nz,fp_diskboot ; handle disk boot
|
|
;
|
|
fp_romboot:
|
|
; Handle FP ROM boot
|
|
ld a,(switches) ; get switches value
|
|
and SW_OPT ; isolate options bits
|
|
ld hl,fpapps ; rom apps cmd char list
|
|
call addhla ; point to the right one
|
|
ld a,(hl) ; get it
|
|
;
|
|
; Attempt ROM application launch
|
|
ld ix,(ra_tbl_loc) ; point to start of ROM app tbl
|
|
ld c,a ; save command in C
|
|
fp_romboot1:
|
|
ld a,(ix+ra_conkey) ; get match char
|
|
and ~$80 ; clear "hidden entry" bit
|
|
cp c ; compare
|
|
jp z,romload ; if match, load it
|
|
ld de,ra_entsiz ; table entry size
|
|
add ix,de ; bump IX to next entry
|
|
ld a,(ix) ; check for end
|
|
or (ix+1) ; ... of table
|
|
jr nz,fp_romboot1 ; loop till done
|
|
ret ; no match, return
|
|
;
|
|
fpapps .db "MBFPCZNU"
|
|
;
|
|
fp_diskboot:
|
|
; get count of disk units
|
|
ld b,BF_SYSGET ; HBIOS Get function
|
|
ld c,BF_SYSGET_DIOCNT ; HBIOS DIO Count sub fn
|
|
rst 08 ; call HBIOS
|
|
ld a,e ; count to A
|
|
ld (diskcnt),a ; save it
|
|
or a ; set flags
|
|
ret z ; bort if no disk units
|
|
ld a,(switches) ; get switches value
|
|
and SW_FLOP ; floppy switch bit
|
|
jr nz,fp_flopboot ; handle auto flop boot
|
|
; fall thru for auto hd boot
|
|
;
|
|
fp_hdboot:
|
|
; Find the first hd with media and boot to that unit using
|
|
; the slice specified by the FP switches.
|
|
ld a,(diskcnt) ; get disk count
|
|
ld b,a ; init loop counter
|
|
ld c,0 ; init disk index
|
|
fp_hdboot1:
|
|
push bc ; save loop control
|
|
ld b,BF_DIODEVICE ; HBIOS Disk Device func
|
|
rst 08 ; unit in C, do it
|
|
bit 5,C ; high capacity disk?
|
|
pop bc ; restore loop control
|
|
jr z,fp_hdboot2 ; if not, continue loop
|
|
push bc ; save loop control
|
|
ld b,BF_DIOMEDIA ; HBIOS Sense Media
|
|
ld e,1 ; perform media discovery
|
|
rst 08 ; do it
|
|
pop bc ; restore loop control
|
|
jr z,fp_hdboot3 ; if has media, go boot it
|
|
fp_hdboot2:
|
|
inc c ; else next disk
|
|
djnz fp_hdboot1 ; loop thru all disks
|
|
ret ; nothing works, abort
|
|
;
|
|
fp_hdboot3:
|
|
ld a,c ; disk unit to A
|
|
ld (bootunit),a ; save it
|
|
ld a,(switches) ; get switches value
|
|
and SW_OPT ; isolate slice value
|
|
ld (bootslice),a ; save it
|
|
jp diskboot ; do it
|
|
;
|
|
fp_flopboot:
|
|
; Find the nth floppy drive and boot to that unit. The
|
|
; floppy number is based on the option switches.
|
|
ld a,(diskcnt) ; get disk count
|
|
ld b,a ; init loop counter
|
|
ld c,0 ; init disk index
|
|
ld a,(switches) ; get switches value
|
|
and SW_OPT ; isolate option bits
|
|
ld e,a ; floppy unit down counter
|
|
inc e ; pre-increment for ZF check
|
|
fp_flopboot1:
|
|
push bc ; save loop control
|
|
push de ; save floppy down ctr
|
|
ld b,BF_DIODEVICE ; HBIOS Disk Device func
|
|
rst 08 ; unit in C, do it
|
|
bit 7,c ; floppy device?
|
|
pop de ; restore loop control
|
|
pop bc ; restore floppy down ctr
|
|
jr z,fp_flopboot3 ; if not floppy, skip
|
|
dec e ; decrement down ctr
|
|
jr z,fp_flopboot2 ; if ctr expired, boot this unit
|
|
fp_flopboot3:
|
|
inc c ; else next disk
|
|
djnz fp_flopboot1 ; loop thru all disks
|
|
ret ; nothing works, abort
|
|
;
|
|
fp_flopboot2:
|
|
ld a,c ; disk unit to A
|
|
ld (bootunit),a ; save it
|
|
xor a ; ; zero accum
|
|
ld (bootslice),a ; floppy boot slice is always 0
|
|
jp diskboot ; do it
|
|
;
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Process a DSKY command from key in A
|
|
;=======================================================================
|
|
;
|
|
#if (DSKYENABLE)
|
|
;
|
|
dskycmd:
|
|
;
|
|
call dsky_getkey ; get DSKY key
|
|
ld a,e ; put in A
|
|
cp $FF ; check for error
|
|
ret z ; abort if so
|
|
;
|
|
push af
|
|
call dsky_highlightkeysoff
|
|
call dsky_l2off
|
|
pop af
|
|
;
|
|
; Attempt built-in commands
|
|
cp KY_BO ; reboot system
|
|
jp z,reboot ; if so, do it
|
|
;
|
|
; Attempt ROM application launch
|
|
ld ix,(ra_tbl_loc) ; point to start of ROM app tbl
|
|
ld c,a ; save DSKY key in C
|
|
dskycmd1:
|
|
ld a,(ix+ra_dskykey) ; get match char
|
|
cp c ; compare
|
|
jp z,romload ; if match, load it
|
|
ld de,ra_entsiz ; table entry size
|
|
add ix,de ; bump IX to next entry
|
|
ld a,(ix) ; check for end
|
|
or (ix+1) ; ... of table
|
|
jr nz,dskycmd1 ; loop till done
|
|
;
|
|
; Attempt disk boot
|
|
ld a,c ; copy key to A
|
|
cp KY_F + 1 ; over max?
|
|
ret nc ; abort if so
|
|
ld (bootunit),a ; set as boot unit
|
|
xor a ; zero A
|
|
ld (bootslice),a ; boot slice always zero here
|
|
jp diskboot ; go do it
|
|
;
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Special command processing
|
|
;=======================================================================
|
|
;
|
|
; Display Help
|
|
;
|
|
help:
|
|
ld hl,str_help ; point to help string
|
|
call pstr ; display it
|
|
ret
|
|
;
|
|
; List ROM apps
|
|
;
|
|
applst:
|
|
ld hl,str_applst
|
|
call pstr
|
|
call nl
|
|
ld ix,(ra_tbl_loc)
|
|
applst1:
|
|
; check for end of table
|
|
ld a,(ix)
|
|
or (ix+1)
|
|
ret z
|
|
;
|
|
ld a,(ix+ra_conkey)
|
|
bit 7,a
|
|
jr nz,applst2
|
|
push af
|
|
call nl
|
|
ld a,' '
|
|
call cout
|
|
call cout
|
|
pop af
|
|
call cout
|
|
ld a,':'
|
|
call cout
|
|
ld a,' '
|
|
call cout
|
|
ld l,(ix+ra_name)
|
|
ld h,(ix+ra_name+1)
|
|
call pstr
|
|
;
|
|
applst2:
|
|
ld bc,ra_entsiz
|
|
add ix,bc
|
|
jr applst1
|
|
|
|
ret
|
|
;
|
|
; Device list
|
|
;
|
|
devlst:
|
|
jp prtall ; do it
|
|
;
|
|
; Set console interface unit
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
setcon:
|
|
; On entry DE is expected to be pointing to start
|
|
; of command. Get unit number.
|
|
call findws ; skip command
|
|
call skipws ; and skip it
|
|
call isnum ; do we have a number?
|
|
jp nz,err_invcmd ; if not, invalid
|
|
call getnum ; parse number into A
|
|
jp c,err_nocon ; handle overflow error
|
|
;
|
|
; Check against max char unit
|
|
ld hl,ciocnt
|
|
cp (hl)
|
|
jp nc,err_nocon ; handle invalid unit
|
|
ld (newcon),a ; save validated console
|
|
;
|
|
; Get baud rate
|
|
call findws
|
|
call skipws ; skip whitespace
|
|
call isnum ; do we have a number?
|
|
jp nz,docon ; if no we don't change baudrate
|
|
push de ; move char ptr
|
|
pop ix ; ... to IX
|
|
call getnum32 ; get 32-bit number
|
|
jp c,err_invcmd ; handle overflow
|
|
ld c,75 ; Constant for baud rate encode
|
|
call encode ; encode into C:4-0
|
|
jp nz,err_invcmd ; handle encoding error
|
|
ld a,c ; move encoded value to A
|
|
ld (newspeed),a ; save validated baud rate
|
|
;
|
|
; Get the current settings for chosen console
|
|
ld b,BF_CIOQUERY ; BIOS serial device query
|
|
ld a,(newcon) ; get device unit num
|
|
ld c,a ; ... and put in C
|
|
rst 08 ; call H/UBIOS, DE := line characteristics
|
|
jp nz,err_invcmd ; abort on error
|
|
;
|
|
ld a,d ; mask off current
|
|
and $11100000 ; baud rate
|
|
ld hl,newspeed ; and load in new
|
|
or (hl) ; baud rate
|
|
ld d,a
|
|
;
|
|
ld hl,str_chspeed ; notify user
|
|
call pstr ; to change speed
|
|
call ldelay ; time for line to flush
|
|
;
|
|
ld b,BF_CIOINIT ; BIOS serial init
|
|
ld a,(newcon) ; get serial device unit
|
|
ld c,a ; ... into C
|
|
rst 08 ; call HBIOS
|
|
jp nz,err_invcmd ; handle error
|
|
;
|
|
call cin ; wait for char at new speed
|
|
;
|
|
; Notify user, we're outta here....
|
|
docon: ld hl,str_newcon ; new console msg
|
|
call pstr ; print string on cur console
|
|
ld a,(newcon) ; restore new console unit
|
|
call prtdecb ; print unit num
|
|
;
|
|
; Set console unit
|
|
ld (curcon),a ; update loader console unit
|
|
ld b,BF_SYSPOKE ; HBIOS func: POKE
|
|
ld d,BID_BIOS ; BIOS bank
|
|
ld e,a ; Char unit value
|
|
ld hl,HCB_LOC + HCB_CONDEV ; Con unit num in HCB
|
|
rst 08 ; do it
|
|
;
|
|
; Display loader prompt on new console
|
|
call nl2 ; formatting
|
|
ld hl,str_banner ; display boot banner
|
|
call pstr ; do it
|
|
ret
|
|
;
|
|
#endif
|
|
;
|
|
; Set RomWBW HBIOS Diagnostic Level
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
setdl:
|
|
; On entry DE is expected to be pointing to start
|
|
; of command
|
|
call findws ; skip command
|
|
call skipws ; and skip it
|
|
or a ; set flags to check for null
|
|
jr z,showdl ; no parm, just display
|
|
call isnum ; do we have a number?
|
|
jp nz,err_invcmd ; if not, invalid
|
|
call getnum ; parse number into A
|
|
jp c,err_invcmd ; handle overflow error
|
|
;
|
|
; Set diagnostic level
|
|
ld b,BF_SYSPOKE ; HBIOS func: POKE
|
|
ld d,BID_BIOS ; BIOS bank
|
|
ld e,a ; diag level value
|
|
ld hl,HCB_LOC + HCB_DIAGLVL ; offset into HCB
|
|
rst 08 ; do it
|
|
; Fall thru to display new value
|
|
;
|
|
showdl:
|
|
; Display current diagnostic level
|
|
ld hl,str_diaglvl ; diag level tag
|
|
call pstr ; print it
|
|
ld b,BF_SYSPEEK ; HBIOS func: PEEK
|
|
ld d,BID_BIOS ; BIOS bank
|
|
ld hl,HCB_LOC + HCB_DIAGLVL ; offset into HCB
|
|
rst 08 ; do it, E := level value
|
|
ld a,e ; put in accum
|
|
call prtdecb ; print it
|
|
ret ; done
|
|
;
|
|
#endif
|
|
;
|
|
; Restart system
|
|
;
|
|
reboot:
|
|
ld hl,str_reboot ; point to message
|
|
call pstr ; print it
|
|
call ldelay ; wait for message to display
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
;ld hl,msg_boot ; point to boot message
|
|
;call dsky_show ; display message
|
|
ld c,DSKY_MSG_LDR_BOOT ; point to boot message
|
|
call dsky_msg ; display message
|
|
;
|
|
; cold boot system
|
|
ld b,BF_SYSRESET ; system restart
|
|
ld c,BF_SYSRES_COLD ; cold start
|
|
rst 08 ; do it, no return
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
; switch to rom bank 0 and jump to address 0
|
|
ld bc,$01FB ; UNA func = set bank
|
|
ld de,0 ; ROM bank 0
|
|
rst 08 ; do it
|
|
jp 0 ; jump to restart address
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Load and run a ROM application, IX=ROM app table entry
|
|
;=======================================================================
|
|
;
|
|
romload:
|
|
;
|
|
; Notify user
|
|
ld hl,str_load
|
|
call pstr
|
|
ld l,(ix+ra_name)
|
|
ld h,(ix+ra_name+1)
|
|
call pstr
|
|
;
|
|
;ld hl,msg_load ; point to load message
|
|
;call dsky_show ; display message
|
|
ld c,DSKY_MSG_LDR_LOAD ; point to load message
|
|
call dsky_msg ; display message
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Copy image to it's running location
|
|
ld a,(ix+ra_bnk) ; get image source bank id
|
|
cp bid_cur ; special value?
|
|
jr nz,romload1 ; if not, continue
|
|
ld a,(bid_ldr) ; else substitute
|
|
romload1:
|
|
push af ; save source bank
|
|
ld e,a ; source bank to E
|
|
ld d,BID_USR ; dest is user bank
|
|
ld l,(ix+ra_siz) ; HL := image size
|
|
ld h,(ix+ra_siz+1) ; ...
|
|
ld b,BF_SYSSETCPY ; HBIOS func: setup bank copy
|
|
rst 08 ; do it
|
|
ld a,'.' ; dot character
|
|
call cout ; show progress
|
|
ld e,(ix+ra_dest) ; DE := run dest adr
|
|
ld d,(ix+ra_dest+1) ; ...
|
|
ld l,(ix+ra_src) ; HL := image source adr
|
|
ld h,(ix+ra_src+1) ; ...
|
|
ld b,BF_SYSBNKCPY ; HBIOS func: bank copy
|
|
rst 08 ; do it
|
|
ld a,'.' ; dot character
|
|
call cout ; show progress
|
|
;
|
|
; Record boot information
|
|
pop af ; recover source bank
|
|
ld l,a ; L := source bank
|
|
ld de,$0000 ; boot vol=0, slice=0
|
|
ld b,BF_SYSSET ; HBIOS func: system set
|
|
ld c,BF_SYSSET_BOOTINFO ; BBIOS subfunc: boot info
|
|
rst 08 ; do it
|
|
ld a,'.' ; dot character
|
|
call cout ; show progress
|
|
;
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
;
|
|
; Note: UNA has no interbank memory copy, so we can only load
|
|
; images from the current bank. We switch to the original bank
|
|
; use a simple ldir to relocate the image, then switch back to the
|
|
; user bank to launch. This will only work if the images are in
|
|
; the lower 32K and the relocation adr is in the upper 32K.
|
|
;
|
|
; Switch to original bank
|
|
ld bc,$01FB ; UNA func: set bank
|
|
ld de,(bid_ldr) ; select user bank
|
|
rst 08 ; do it
|
|
ld a,'.' ; dot character
|
|
call cout ; show progress
|
|
;
|
|
; Copy image to running location
|
|
ld l,(ix+ra_src) ; HL := image source adr
|
|
ld h,(ix+ra_src+1) ; ...
|
|
ld e,(ix+ra_dest) ; DE := run dest adr
|
|
ld d,(ix+ra_dest+1) ; ...
|
|
ld c,(ix+ra_siz) ; BC := image size
|
|
ld b,(ix+ra_siz+1) ; ...
|
|
ldir ; copy image
|
|
ld a,'.' ; dot character
|
|
call cout ; show progress
|
|
;
|
|
; Switch back to user bank
|
|
ld bc,$01FB ; UNA func: set bank
|
|
ld de,(bid_ldr) ; select user bank
|
|
rst 08 ; do it
|
|
ld a,'.' ; dot character
|
|
call cout ; show progress
|
|
;
|
|
; Record boot information
|
|
ld de,(bid_ldr) ; original bank
|
|
ld l,$01 ; encoded boot slice/unit
|
|
ld bc,$01FC ; UNA func: set bootstrap hist
|
|
rst 08 ; call una
|
|
;
|
|
#endif
|
|
;
|
|
;ld hl,msg_go ; point to go message
|
|
;call dsky_show ; display message
|
|
ld c,DSKY_MSG_LDR_GO ; point to go message
|
|
call dsky_msg ; display message
|
|
;
|
|
ld l,(ix+ra_ent) ; HL := app entry address
|
|
ld h,(ix+ra_ent+1) ; ...
|
|
jp (hl) ; go
|
|
;
|
|
;=======================================================================
|
|
; Boot disk unit/slice
|
|
;=======================================================================
|
|
;
|
|
diskboot:
|
|
;
|
|
; Notify user
|
|
ld hl,str_boot1 ; "Booting Disk Unit"
|
|
call pstr
|
|
ld a,(bootunit)
|
|
call prtdecb
|
|
ld hl,str_boot2 ; "Slice"
|
|
call pstr
|
|
ld a,(bootslice)
|
|
call prtdecb
|
|
;
|
|
;ld hl,msg_load ; point to load message
|
|
;call dsky_show ; display message
|
|
ld c,DSKY_MSG_LDR_LOAD ; point to load message
|
|
call dsky_msg ; display message
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Get Extended information for the Device, and Slice
|
|
ld b,BF_EXTSLICE ; HBIOS func: SLICE CALC
|
|
ld a,(bootunit) ; passing boot unit
|
|
ld d,a
|
|
ld a,(bootslice) ; and slice
|
|
ld e,a
|
|
rst 08 ; do it
|
|
;
|
|
; 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_nomedia ; handle the error
|
|
cp ERR_RANGE ; slice is invalid
|
|
jp z,err_badslice ; bad slice, handle err
|
|
or a ; any other error
|
|
jp nz,err_diskio ; handle as general IO error
|
|
;
|
|
diskboot0:
|
|
ld a,c ; media id to A
|
|
ld (mediaid),a ; save media id
|
|
;
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
;
|
|
; Check that drive actually exists
|
|
ld a,(bootunit) ; get disk unit to boot
|
|
ld b,a ; put in B for func call
|
|
ld c,$48 ; UNA func: get disk type
|
|
rst 08 ; call UNA, B preserved
|
|
jp nz,err_nodisk ; handle error if no such disk
|
|
;
|
|
; If non-zero slice requested, confirm device can handle it
|
|
ld a,(bootslice) ; get slice
|
|
or a ; set flags
|
|
jr z,diskboot0 ; slice 0, skip slice check
|
|
ld a,d ; disk type to A
|
|
cp $41 ; IDE?
|
|
jr z,diskboot0 ; if so, OK
|
|
cp $42 ; PPIDE?
|
|
jr z,diskboot0 ; if so, OK
|
|
cp $43 ; SD?
|
|
jr z,diskboot0 ; if so, OK
|
|
cp $44 ; DSD?
|
|
jr z,diskboot0 ; if so, OK
|
|
jp err_noslice ; no such slice, handle err
|
|
;
|
|
diskboot0:
|
|
; Below is wrong. It assumes we are booting from a hard
|
|
; disk, but it could also be a RAM/ROM disk. However, it is
|
|
; not actually possible to boot from those, so not gonna
|
|
; worry about this.
|
|
ld a,4 ; assume legacy hard disk
|
|
ld (mediaid),a ; save media id
|
|
;
|
|
diskboot1:
|
|
; Initialize working LBA value
|
|
ld hl,0 ; zero HL
|
|
ld (lba),hl ; init
|
|
ld (lba+2),hl ; ... LBA
|
|
;
|
|
; Set legacy sectors per slice
|
|
ld hl,16640 ; legacy sectors per slice
|
|
ld (sps),hl ; save it
|
|
;
|
|
; Check for hard disk
|
|
ld a,(mediaid) ; load media id
|
|
cp 4 ; legacy hard disk?
|
|
jr nz,diskboot8 ; if not hd, no part table
|
|
;
|
|
; Attempt to read MBR
|
|
ld de,0 ; MBR is at
|
|
ld hl,0 ; ... first sector
|
|
ld bc,bl_mbrsec ; read into MBR buffer
|
|
ld (dma),bc ; save
|
|
ld b,1 ; one sector
|
|
ld a,(bootunit) ; get bootunit
|
|
ld c,a ; put in C
|
|
call diskread ; do it
|
|
ret nz ; abort on error
|
|
;
|
|
; Check signature
|
|
ld hl,(bl_mbrsec+$1FE) ; get signature
|
|
ld a,l ; first byte
|
|
cp $55 ; should be $55
|
|
jr nz,diskboot4 ; if not, no part table
|
|
ld a,h ; second byte
|
|
cp $AA ; should be $AA
|
|
jr nz,diskboot4 ; if not, no part table
|
|
;
|
|
; Try to find our entry in part table and capture lba offset
|
|
ld b,4 ; four entries in part table
|
|
ld hl,bl_mbrsec+$1BE+4 ; offset of first entry part type
|
|
diskboot2:
|
|
ld a,(hl) ; get part type
|
|
cp $2E ; cp/m partition?
|
|
jr z,diskboot3 ; cool, grab the lba offset
|
|
ld de,16 ; part table entry size
|
|
add hl,de ; bump to next entry part type
|
|
djnz diskboot2 ; loop thru table
|
|
jr diskboot4 ; too bad, no cp/m partition
|
|
;
|
|
diskboot3:
|
|
; Capture the starting LBA of the CP/M partition we found
|
|
ld de,4 ; LBA is 4 bytes after part type
|
|
add hl,de ; point to it
|
|
ld de,lba ; loc to store lba offset
|
|
ld bc,4 ; 4 bytes (32 bits)
|
|
ldir ; copy it
|
|
; If boot from partition, use new sectors per slice value
|
|
ld hl,16384 ; new sectors per slice
|
|
ld (sps),hl ; save it
|
|
;
|
|
diskboot4:
|
|
; Add slice offset
|
|
ld a,(bootslice) ; get boot slice, A is loop cnt
|
|
ld hl,(lba) ; set DE:HL
|
|
ld de,(lba+2) ; ... to starting LBA
|
|
ld bc,(sps) ; sectors per slice
|
|
diskboot5:
|
|
or a ; set flags to check loop ctr
|
|
jr z,diskboot7 ; done if counter exhausted
|
|
add hl,bc ; add one slice to low word
|
|
jr nc,diskboot6 ; check for carry
|
|
inc de ; if so, bump high word
|
|
diskboot6:
|
|
dec a ; dec loop downcounter
|
|
jr diskboot5 ; and loop
|
|
;
|
|
#endif
|
|
;
|
|
diskboot7:
|
|
ld (lba),hl ; update lba, low word
|
|
ld (lba+2),de ; update lba, high word
|
|
;
|
|
diskboot8:
|
|
; Note that we could be coming from diskboot1!
|
|
ld hl,str_ldsec ; display prefix "Sector Ox"
|
|
call pstr ; do it
|
|
ld hl,(lba) ; recover lba loword
|
|
ld de,(lba+2) ; recover lba hiword
|
|
call prthex32 ; display starting sector
|
|
call pdot ; show progress
|
|
;
|
|
; Read boot info sector, third sector
|
|
ld bc,2 ; sector offset
|
|
add hl,bc ; add to LBA value low word
|
|
jr nc,diskboot9 ; check for carry
|
|
inc de ; if so, bump high word
|
|
diskboot9:
|
|
ld bc,bl_infosec ; read buffer
|
|
ld (dma),bc ; save
|
|
ld a,(bootunit) ; disk unit to read
|
|
ld c,a ; put in C
|
|
ld b,1 ; one sector
|
|
call diskread ; do it
|
|
ret nz ; abort on error
|
|
call pdot ; show progress
|
|
;
|
|
; Check signature
|
|
ld de,(bb_sig) ; get signature read
|
|
ld a,$A5 ; expected value of first byte
|
|
cp d ; compare
|
|
jp nz,err_sig ; handle error
|
|
ld a,$5A ; expected value of second byte
|
|
cp e ; compare
|
|
jp nz,err_sig ; handle error
|
|
call pdot ; show progress
|
|
;
|
|
; Print disk boot info
|
|
; Volume "xxxxxxx" (0xXXXX-0xXXXX, entry @ 0xXXXX)
|
|
ld hl,str_binfo1 ; load string
|
|
call pstr ; print
|
|
push hl ; save string ptr
|
|
ld hl,bb_label ; point to label
|
|
call pvol ; print it
|
|
pop hl ; restore string ptr
|
|
call pstr ; print
|
|
push hl ; save string ptr
|
|
ld bc,(bb_cpmloc) ; get load loc
|
|
call prthexword ; print it
|
|
pop hl ; restore string ptr
|
|
call pstr ; print
|
|
push hl ; save string ptr
|
|
ld bc,(bb_cpmend) ; get load end
|
|
call prthexword ; print it
|
|
pop hl ; restore string ptr
|
|
call pstr ; print
|
|
push hl ; save string ptr
|
|
ld bc,(bb_cpment) ; get load end
|
|
call prthexword ; print it
|
|
pop hl ; restore string ptr
|
|
call pstr ; print
|
|
;
|
|
; Compute number of sectors to load
|
|
ld hl,(bb_cpmend) ; hl := end
|
|
ld de,(bb_cpmloc) ; de := start
|
|
or a ; clear carry
|
|
sbc hl,de ; hl := length to load
|
|
; If load length is not a multiple of sector size (512)
|
|
; we need to round up to get everything loaded!
|
|
ld de,511 ; 1 less than sector size
|
|
add hl,de ; ... and roundup
|
|
ld a,h ; determine 512 byte sector count
|
|
rra ; ... by dividing msb by two
|
|
ld (loadcnt),a ; ... and save it
|
|
call pdot ; show progress
|
|
;
|
|
; Start OS load at sector 3
|
|
ld hl,(lba) ; low word of saved LBA
|
|
ld de,(lba+2) ; high word of saved LBA
|
|
ld bc,3 ; offset for sector 3
|
|
add hl,bc ; apply it
|
|
jr nc,diskboot10 ; check for carry
|
|
inc de ; bump high word if so
|
|
diskboot10:
|
|
ld bc,(bb_cpmloc) ; load address
|
|
ld (dma),bc ; and save it
|
|
ld a,(loadcnt) ; get sectors to read
|
|
ld b,a ; put in B
|
|
ld a,(bootunit) ; get boot disk unit
|
|
ld c,a ; put in C
|
|
call diskread ; read image
|
|
ret nz ; abort on error
|
|
call pdot ; show progress
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Record boot unit/slice
|
|
ld b,BF_SYSSET ; hb func: set hbios parameter
|
|
ld c,BF_SYSSET_BOOTINFO ; hb subfunc: set boot info
|
|
ld a,(bid_ldr) ; original bank is boot bank
|
|
ld l,a ; ... and save as boot bank
|
|
ld a,(bootunit) ; load boot unit
|
|
ld d,a ; save in D
|
|
ld a,(bootslice) ; load boot slice
|
|
ld e,a ; save in E
|
|
rst 08
|
|
jp nz,err_api ; handle errors
|
|
;
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
;
|
|
; Record boot unit/slice
|
|
; UNA provides only a single byte to record the boot unit
|
|
; so we encode the unit/slice into one byte by using the
|
|
; high nibble for unit and low nibble for slice.
|
|
ld de,-1 ; boot rom page, -1 for n/a
|
|
ld a,(bootslice) ; get boot slice
|
|
and $0F ; 4 bits only
|
|
rlca ; rotate to high bits
|
|
rlca ; ...
|
|
rlca ; ...
|
|
rlca ; ...
|
|
ld l,a ; put in L
|
|
ld a,(bootunit) ; get boot disk unit
|
|
and $0F ; 4 bits only
|
|
or l ; combine
|
|
ld l,a ; back to L
|
|
ld bc,$01FC ; UNA func: set bootstrap hist
|
|
rst 08 ; call UNA
|
|
jp nz,err_api ; handle error
|
|
;
|
|
#endif
|
|
;
|
|
call pdot ; show progress
|
|
;
|
|
;ld hl,msg_go ; point to go message
|
|
;call dsky_show ; display message
|
|
ld c,DSKY_MSG_LDR_GO ; point to go message
|
|
call dsky_msg ; display message
|
|
;
|
|
; Jump to entry vector
|
|
ld hl,(bb_cpment) ; get entry vector
|
|
jp (hl) ; and go there
|
|
;
|
|
;-----------------------------------------------------------------------
|
|
;
|
|
; Read disk sector(s)
|
|
; DE:HL is LBA, B is sector count, C is disk unit
|
|
;
|
|
diskread:
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
;
|
|
; Seek to requested sector in DE:HL
|
|
push bc ; save unit and count
|
|
ld b,c ; unit to read in B
|
|
ld c,$41 ; UNA func: set lba
|
|
rst 08 ; set lba
|
|
pop bc ; recover unit and count
|
|
jp nz,err_api ; handle error
|
|
;
|
|
; Read sector(s) into buffer
|
|
ld l,b ; sectors to read
|
|
ld b,c ; unit to read in B
|
|
ld c,$42 ; UNA func: read sectors
|
|
ld de,(dma) ; dest for read
|
|
rst 08 ; do read
|
|
jp nz,err_diskio ; handle error
|
|
xor a ; signal success
|
|
ret ; and done
|
|
;
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Seek to requested sector in DE:HL
|
|
push bc ; save unit & count
|
|
set 7,d ; set LBA access flag
|
|
ld b,BF_DIOSEEK ; HBIOS func: seek
|
|
rst 08 ; 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 d,BID_USR ; user bank
|
|
rst 08 ; do it
|
|
jp nz,err_diskio ; handle error
|
|
xor a ; signal success
|
|
ret ; and done
|
|
;
|
|
#endif
|
|
;
|
|
; Built-in mini-loader for S100 Monitor. The S100 platform build
|
|
; imbeds the S100 Monitor in the ROM at the start of bank 3 (BID_IMG2).
|
|
; This bit of code just launches the monitor directly from that bank.
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
#if (PLATFORM == PLT_S100)
|
|
;
|
|
s100mon:
|
|
; Warn user that console is being directed to the S100 bus
|
|
; if the IOBYTE bit 0 is 0 (%xxxxxxx0).
|
|
in a,($75) ; get IO byte
|
|
and %00000001 ; isolate console bit
|
|
jr nz,s100mon1 ; if 0, bypass msg
|
|
ld hl,str_s100con ; console msg string
|
|
call pstr ; display it
|
|
;
|
|
s100mon1:
|
|
; Launch S100 Monitor from ROM Bank 3
|
|
call ldelay ; wait for UART buf to empty
|
|
di ; suspend interrupts
|
|
ld a,BID_IMG2 ; S100 monitor bank
|
|
ld ix,0 ; execution resumes here
|
|
jp HB_BNKCALL ; do it
|
|
;
|
|
str_smon .db "S100 Z180 Monitor",0
|
|
str_s100con .db "\r\n\r\nConsole on S100 Bus",0
|
|
;
|
|
#endif
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Utility functions
|
|
;=======================================================================
|
|
;
|
|
; Print string at HL on console, null terminated
|
|
;
|
|
pstr:
|
|
ld a,(hl) ; get next character
|
|
or a ; set flags
|
|
inc hl ; bump pointer regardless
|
|
ret z ; done if null
|
|
call cout ; display character
|
|
jr pstr ; loop till done
|
|
;
|
|
; Print volume label string at HL, '$' terminated, 16 chars max
|
|
;
|
|
pvol:
|
|
ld b,16 ; init max char downcounter
|
|
pvol1:
|
|
ld a,(hl) ; get next character
|
|
cp '$' ; set flags
|
|
inc hl ; bump pointer regardless
|
|
ret z ; done if null
|
|
call cout ; display character
|
|
djnz pvol1 ; loop till done
|
|
ret ; hit max of 16 chars
|
|
;
|
|
; Start a newline on console (cr/lf)
|
|
;
|
|
nl2:
|
|
call nl ; double newline
|
|
nl:
|
|
push af
|
|
ld a,cr ; cr
|
|
call cout ; send it
|
|
ld a,lf ; lf
|
|
call cout ; send it and return
|
|
pop af
|
|
ret
|
|
;
|
|
; Print a dot on console
|
|
;
|
|
pdot:
|
|
push af
|
|
ld a,'.'
|
|
call cout
|
|
pop af
|
|
ret
|
|
;
|
|
; Read a string on the console
|
|
;
|
|
; Uses address $0080 in page zero for buffer
|
|
; Input is zero terminated
|
|
;
|
|
rdln:
|
|
ld de,cmdbuf ; init buffer address ptr
|
|
rdln_nxt:
|
|
call cin ; get a character
|
|
cp bs ; backspace?
|
|
jr z,rdln_bs ; handle it if so
|
|
cp del ; del/rubout?
|
|
jr z,rdln_bs ; handle as backspace
|
|
cp cr ; return?
|
|
jr z,rdln_cr ; handle it if so
|
|
;
|
|
; check for non-printing characters
|
|
cp ' ' ; first printable is space char
|
|
jr c,rdln_bel ; too low, beep and loop
|
|
cp '~'+1 ; last printable char
|
|
jr nc,rdln_bel ; too high, beep and loop
|
|
;
|
|
; need to check for buffer overflow here!!!
|
|
ld hl,cmdbuf+cmdmax ; max cmd length
|
|
or a ; clear carry
|
|
sbc hl,de ; test for max
|
|
jr z,rdln_bel ; at max, beep and loop
|
|
;
|
|
; good to go, echo and store character
|
|
call cout ; echo character input
|
|
ld (de),a ; save in buffer
|
|
inc de ; inc buffer ptr
|
|
jr rdln_nxt ; loop till done
|
|
;
|
|
rdln_bs:
|
|
ld hl,cmdbuf ; start of buffer
|
|
or a ; clear carry
|
|
sbc hl,de ; subtract from cur buf ptr
|
|
jr z,rdln_bel ; at buf start, just beep
|
|
ld hl,str_bs ; backspace sequence
|
|
call pstr ; send it
|
|
dec de ; backup buffer pointer
|
|
jr rdln_nxt ; and loop
|
|
;
|
|
rdln_bel:
|
|
ld a,bel ; Bell characters
|
|
call cout ; send it
|
|
jr rdln_nxt ; and loop
|
|
;
|
|
rdln_cr:
|
|
xor a ; null to A
|
|
ld (de),a ; store terminator
|
|
ret ; and return
|
|
;
|
|
; Find next whitespace character at buffer adr in DE, returns with first
|
|
; whitespace character in A.
|
|
;
|
|
findws:
|
|
ld a,(de) ; get next char
|
|
or a ; check for eol
|
|
ret z ; done if so
|
|
cp ' ' ; blank?
|
|
ret z ; nope, done
|
|
inc de ; bump buffer pointer
|
|
jr findws ; and loop
|
|
;
|
|
; Skip whitespace at buffer adr in DE, returns with first
|
|
; non-whitespace character in A.
|
|
;
|
|
skipws:
|
|
ld a,(de) ; get next char
|
|
or a ; check for eol
|
|
ret z ; done if so
|
|
cp ' ' ; blank?
|
|
ret nz ; nope, done
|
|
inc de ; bump buffer pointer
|
|
jr skipws ; and loop
|
|
;
|
|
; Uppercase character in A
|
|
;
|
|
upcase:
|
|
cp 'a' ; below 'a'?
|
|
ret c ; if so, nothing to do
|
|
cp 'z'+1 ; above 'z'?
|
|
ret nc ; if so, nothing to do
|
|
and ~$20 ; convert character to lower
|
|
ret ; done
|
|
;
|
|
; Get numeric chars at DE and convert to number returned in A
|
|
; Carry flag set on overflow
|
|
;
|
|
getnum:
|
|
ld c,0 ; C is working register
|
|
getnum1:
|
|
ld a,(de) ; get the active char
|
|
cp '0' ; compare to ascii '0'
|
|
jr c,getnum2 ; abort if below
|
|
cp '9' + 1 ; compare to ascii '9'
|
|
jr nc,getnum2 ; abort if above
|
|
;
|
|
; valid digit, add new digit to C
|
|
ld a,c ; get working value to A
|
|
rlca ; multiply by 10
|
|
ret c ; overflow, return with carry set
|
|
rlca ; ...
|
|
ret c ; overflow, return with carry set
|
|
add a,c ; ...
|
|
ret c ; overflow, return with carry set
|
|
rlca ; ...
|
|
ret c ; overflow, return with carry set
|
|
ld c,a ; back to C
|
|
ld a,(de) ; get new digit
|
|
sub '0' ; make binary
|
|
add a,c ; add in working value
|
|
ret c ; overflow, return with carry set
|
|
ld c,a ; back to C
|
|
;
|
|
inc de ; bump to next char
|
|
jr getnum1 ; loop
|
|
;
|
|
getnum2: ; return result
|
|
ld a,c ; return result in A
|
|
or a ; with flags set, CF is cleared
|
|
ret
|
|
;
|
|
; Is character in A numeric? NZ if not
|
|
;
|
|
isnum:
|
|
cp '0' ; compare to ascii '0'
|
|
jr c,isnum1 ; abort if below
|
|
cp '9' + 1 ; compare to ascii '9'
|
|
jr nc,isnum1 ; abort if above
|
|
cp a ; set Z
|
|
ret
|
|
isnum1:
|
|
or $FF ; set NZ
|
|
ret ; and done
|
|
;
|
|
; Delay 16us (cpu speed compensated) including call/ret invocation
|
|
; Register A and flags destroyed
|
|
; No compensation for z180 memory wait states
|
|
; There is an overhead of 3ts per invocation
|
|
; Impact of overhead diminishes as cpu speed increases
|
|
;
|
|
; cpu scaler (cpuscl) = (cpuhmz - 2) for 16us + 3ts delay
|
|
; note: cpuscl must be >= 1!
|
|
;
|
|
; example: 8mhz cpu (delay goal is 16us)
|
|
; loop = ((6 * 16) - 5) = 91ts
|
|
; total cost = (91 + 40) = 131ts
|
|
; actual delay = (131 / 8) = 16.375us
|
|
;
|
|
; --- total cost = (loop cost + 40) ts -----------------+
|
|
delay: ; 17ts (from invoking call) |
|
|
ld a,(cpuscl) ; 13ts |
|
|
; |
|
|
delay1: ; |
|
|
; --- loop = ((cpuscl * 16) - 5) ts ------------+ |
|
|
dec a ; 4ts | |
|
|
#if (BIOS == BIOS_WBW) ; | |
|
|
#if (CPUFAM == CPU_Z180) ; | |
|
|
or a ; +4ts for z180 | |
|
|
#endif ; | |
|
|
#endif ; | |
|
|
jr nz,delay1 ; 12ts (nz) / 7ts (z) | |
|
|
; ----------------------------------------------+ |
|
|
; |
|
|
ret ; 10ts (return) |
|
|
;-------------------------------------------------------+
|
|
;
|
|
; Delay 16us * DE (cpu speed compensated)
|
|
; Register DE, A, and flags destroyed
|
|
; No compensation for z180 memory wait states
|
|
; There is a 27ts overhead for call/ret per invocation
|
|
; Impact of overhead diminishes as DE and/or cpu speed increases
|
|
;
|
|
; cpu scaler (cpuscl) = (cpuhmz - 2) for 16us outer loop cost
|
|
; note: cpuscl must be > 0!
|
|
;
|
|
; Example: 8MHz cpu, DE=6250 (delay goal is .1 sec or 100,000us)
|
|
; inner loop = ((16 * 6) - 5) = 91ts
|
|
; outer loop = ((91 + 37) * 6250) = 800,000ts
|
|
; actual delay = ((800,000 + 27) / 8) = 100,003us
|
|
;
|
|
; --- total cost = (outer loop + 27) ts ------------------------+
|
|
vdelay: ; 17ts (from invoking call) |
|
|
; |
|
|
; --- outer loop = ((inner loop + 37) * de) ts ---------+ |
|
|
ld a,(cpuscl) ; 13ts | |
|
|
; | |
|
|
vdelay1: ; | |
|
|
; --- inner loop = ((cpuscl * 16) - 5) ts ------+ | |
|
|
#if (BIOS == BIOS_WBW) ; | | |
|
|
#if (CPUFAM == CPU_Z180) ; | | |
|
|
or a ; +4ts for z180 | | |
|
|
#endif ; | | |
|
|
#endif ; | | |
|
|
dec a ; 4ts | | |
|
|
jr nz,vdelay1 ; 12ts (nz) / 7ts (z) | | |
|
|
; ----------------------------------------------+ | |
|
|
; | |
|
|
dec de ; 6ts | |
|
|
#if (BIOS == BIOS_WBW) ; | | |
|
|
#if (CPUFAM == CPU_Z180) ; | |
|
|
or a ; +4ts for z180 | |
|
|
#endif ; | |
|
|
#endif ; | |
|
|
ld a,d ; 4ts | |
|
|
or e ; 4ts | |
|
|
jp nz,vdelay ; 10ts | |
|
|
;-------------------------------------------------------+ |
|
|
; |
|
|
ret ; 10ts (final return) |
|
|
;---------------------------------------------------------------+
|
|
;
|
|
; Delay about 0.5 seconds
|
|
; 500000us / 16us = 31250
|
|
;
|
|
ldelay:
|
|
push af
|
|
push de
|
|
ld de,31250
|
|
call vdelay
|
|
pop de
|
|
pop af
|
|
ret
|
|
;
|
|
; Initialize delay scaler based on operating cpu speed
|
|
; HBIOS *must* be installed and available via rst 8!!!
|
|
; CPU scaler := max(1, (phimhz - 2))
|
|
;
|
|
delay_init:
|
|
#if (BIOS == BIOS_UNA)
|
|
ld c,$F8 ; UNA bios get phi function
|
|
rst 08 ; returns speed in hz in de:hl
|
|
ld b,4 ; divide mhz in de:hl by 100000h
|
|
delay_init0:
|
|
srl d ; ... to get approx cpu speed in
|
|
rr e ; ...mhz. throw away hl, and
|
|
djnz delay_init0 ; ...right shift de by 4.
|
|
inc e ; fix up for value truncation
|
|
ld a,e ; put in a
|
|
#else
|
|
ld b,BF_SYSGET ; HBIOS func=get sys info
|
|
ld c,BF_SYSGET_CPUINFO ; HBIOS subfunc=get cpu info
|
|
rst 08 ; call HBIOS, rst 08 not yet installed
|
|
ld a,l ; put speed in mhz in accum
|
|
#endif
|
|
cp 3 ; test for <= 2 (special handling)
|
|
jr c,delay_init1 ; if <= 2, special processing
|
|
sub 2 ; adjust as required by delay functions
|
|
jr delay_init2 ; and continue
|
|
delay_init1:
|
|
ld a,1 ; use the min value of 1
|
|
delay_init2:
|
|
ld (cpuscl),a ; update cpu scaler value
|
|
ret
|
|
|
|
#if (CPUMHZ < 3)
|
|
cpuscl .db 1 ; cpu scaler must be > 0
|
|
#else
|
|
cpuscl .db CPUMHZ - 2 ; otherwise 2 less than phi mhz
|
|
#endif
|
|
;
|
|
; Print value of a in decimal with leading zero suppression
|
|
;
|
|
prtdecb:
|
|
push hl
|
|
push af
|
|
ld l,a
|
|
ld h,0
|
|
call prtdec
|
|
pop af
|
|
pop hl
|
|
ret
|
|
;
|
|
; Print value of HL in decimal with leading zero suppression
|
|
;
|
|
prtdec:
|
|
push bc
|
|
push de
|
|
push hl
|
|
ld e,'0'
|
|
ld bc,-10000
|
|
call prtdec1
|
|
ld bc,-1000
|
|
call prtdec1
|
|
ld bc,-100
|
|
call prtdec1
|
|
ld c,-10
|
|
call prtdec1
|
|
ld e,0
|
|
ld c,-1
|
|
call prtdec1
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
prtdec1:
|
|
ld a,'0' - 1
|
|
prtdec2:
|
|
inc a
|
|
add hl,bc
|
|
jr c,prtdec2
|
|
sbc hl,bc
|
|
cp e
|
|
jr z,prtdec3
|
|
ld e,0
|
|
call cout
|
|
prtdec3:
|
|
ret
|
|
;
|
|
; Short delay functions. No clock speed compensation, so they
|
|
; will run longer on slower systems. The number indicates the
|
|
; number of call/ret invocations. A single call/ret is
|
|
; 27 t-states on a z80, 25 t-states on a z180.
|
|
;
|
|
; ; z80 z180
|
|
; ; ---- ----
|
|
dly64: call dly32 ; 1728 1600
|
|
dly32: call dly16 ; 864 800
|
|
dly16: call dly8 ; 432 400
|
|
dly8: call dly4 ; 216 200
|
|
dly4: call dly2 ; 108 100
|
|
dly2: call dly1 ; 54 50
|
|
dly1: ret ; 27 25
|
|
;
|
|
; Add hl,a
|
|
;
|
|
; A register is destroyed!
|
|
;
|
|
addhla:
|
|
add a,l
|
|
ld l,a
|
|
ret nc
|
|
inc h
|
|
ret
|
|
;
|
|
; Print the hex byte value in A
|
|
;
|
|
prthexbyte:
|
|
push af
|
|
push de
|
|
call hexascii
|
|
ld a,d
|
|
call cout
|
|
ld a,e
|
|
call cout
|
|
pop de
|
|
pop af
|
|
ret
|
|
;
|
|
; Print the hex word value in BC
|
|
;
|
|
prthexword:
|
|
push af
|
|
ld a,b
|
|
call prthexbyte
|
|
ld a,c
|
|
call prthexbyte
|
|
pop af
|
|
ret
|
|
;
|
|
; Print the hex dword value in DE:HL
|
|
;
|
|
prthex32:
|
|
push bc
|
|
push de
|
|
pop bc
|
|
call prthexword
|
|
push hl
|
|
pop bc
|
|
call prthexword
|
|
pop bc
|
|
ret
|
|
;
|
|
; Convert binary value in A to ASCII hex characters in DE
|
|
;
|
|
hexascii:
|
|
ld d,a
|
|
call hexconv
|
|
ld e,a
|
|
ld a,d
|
|
rlca
|
|
rlca
|
|
rlca
|
|
rlca
|
|
call hexconv
|
|
ld d,a
|
|
ret
|
|
;
|
|
; Convert low nibble of A to ASCII hex
|
|
;
|
|
hexconv:
|
|
and 0Fh ; low nibble only
|
|
add a,90h
|
|
daa
|
|
adc a,40h
|
|
daa
|
|
ret
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Get numeric chars and convert to 32-bit number returned in DE:HL
|
|
; IX points to start of char buffer
|
|
; Carry flag set on overflow
|
|
;
|
|
getnum32:
|
|
ld de,0 ; Initialize DE:HL
|
|
ld hl,0 ; ... to zero
|
|
getnum32a:
|
|
ld a,(ix) ; get the active char
|
|
cp '0' ; compare to ascii '0'
|
|
jr c,getnum32c ; abort if below
|
|
cp '9' + 1 ; compare to ascii '9'
|
|
jr nc,getnum32c ; abort if above
|
|
;
|
|
; valid digit, multiply DE:HL by 10
|
|
; X * 10 = (((x * 2 * 2) + x)) * 2
|
|
push de
|
|
push hl
|
|
;
|
|
call getnum32e ; DE:HL *= 2
|
|
jr c,getnum32d ; if overflow, ret w/ CF & stack pop
|
|
;
|
|
call getnum32e ; DE:HL *= 2
|
|
jr c,getnum32d ; if overflow, ret w/ CF & stack pop
|
|
;
|
|
pop bc ; DE:HL += X
|
|
add hl,bc
|
|
ex de,hl
|
|
pop bc
|
|
adc hl,bc
|
|
ex de,hl
|
|
ret c ; if overflow, ret w/ CF
|
|
;
|
|
call getnum32e ; DE:HL *= 2
|
|
ret c ; if overflow, ret w/ CF
|
|
;
|
|
; now add in new digit
|
|
ld a,(ix) ; get the active char
|
|
sub '0' ; make it binary
|
|
add a,l ; add to L, CF updated
|
|
ld l,a ; back to L
|
|
jr nc,getnum32b ; if no carry, done
|
|
inc h ; otherwise, bump H
|
|
jr nz,getnum32b ; if no overflow, done
|
|
inc e ; otherwise, bump E
|
|
jr nz,getnum32b ; if no overflow, done
|
|
inc d ; otherwise, bump D
|
|
jr nz,getnum32b ; if no overflow, done
|
|
scf ; set carry flag to indicate overflow
|
|
ret ; and return
|
|
;
|
|
getnum32b:
|
|
inc ix ; bump to next char
|
|
jr getnum32a ; loop
|
|
;
|
|
getnum32c:
|
|
; successful completion
|
|
xor a ; clear flags
|
|
ret ; and return
|
|
;
|
|
getnum32d:
|
|
; special overflow exit with stack fixup
|
|
pop hl ; burn 2
|
|
pop hl ; ... stack entries
|
|
ret ; and return
|
|
;
|
|
getnum32e:
|
|
; DE:HL := DE:HL * 2
|
|
sla l
|
|
rl h
|
|
rl e
|
|
rl d
|
|
ret
|
|
;
|
|
; Integer divide DE:HL by C
|
|
; result in DE:HL, remainder in A
|
|
; clobbers F, B
|
|
;
|
|
div32x8:
|
|
xor a
|
|
ld b,32
|
|
div32x8a:
|
|
add hl,hl
|
|
rl e
|
|
rl d
|
|
rla
|
|
cp c
|
|
jr c,div32x8b
|
|
sub c
|
|
inc l
|
|
div32x8b:
|
|
djnz div32x8a
|
|
ret
|
|
;
|
|
DIV32X8 .equ div32x8
|
|
;
|
|
#include "encode.asm" ; baud rate encoding routine
|
|
;
|
|
encode .equ ENCODE
|
|
;
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Console character I/O helper routines (registers preserved)
|
|
;=======================================================================
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Output character from A
|
|
;
|
|
cout:
|
|
; Save all incoming registers
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
;
|
|
; Output character to console via HBIOS
|
|
ld e,a ; output char to E
|
|
ld a,(curcon) ; get current console
|
|
ld c,a ; console unit to C
|
|
ld b,BF_CIOOUT ; HBIOS func: output char
|
|
rst 08 ; HBIOS outputs character
|
|
;
|
|
; Restore all registers
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
;
|
|
; Input character to A
|
|
;
|
|
cin:
|
|
; Save incoming registers (AF is output)
|
|
push bc
|
|
push de
|
|
push hl
|
|
;
|
|
; Input character from console via hbios
|
|
ld a,(curcon) ; get current console
|
|
ld c,a ; console unit to C
|
|
ld b,BF_CIOIN ; HBIOS func: input char
|
|
rst 08 ; HBIOS reads character
|
|
ld a,e ; move character to A for return
|
|
;
|
|
; Restore registers (AF is output)
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
; Return input status in A (0 = no char, != 0 char waiting)
|
|
;
|
|
cst:
|
|
; Save incoming registers (AF is output)
|
|
push bc
|
|
push de
|
|
push hl
|
|
;
|
|
; Get console input status via HBIOS
|
|
ld a,(curcon) ; get current console
|
|
ld c,a ; console unit to C
|
|
ld b,BF_CIOIST ; HBIOS func: input status
|
|
rst 08 ; HBIOS returns status in A
|
|
;
|
|
; Restore registers (AF is output)
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
;
|
|
; Output character from A
|
|
;
|
|
cout:
|
|
; Save all incoming registers
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
;
|
|
; Output character to console via UBIOS
|
|
ld e,a
|
|
ld bc,$12
|
|
rst 08
|
|
;
|
|
; Restore all registers
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
;
|
|
; Input character to A
|
|
;
|
|
cin:
|
|
; Save incoming registers (AF is output)
|
|
push bc
|
|
push de
|
|
push hl
|
|
;
|
|
; Input character from console via UBIOS
|
|
ld bc,$11
|
|
rst 08
|
|
ld a,e
|
|
;
|
|
; Restore registers (AF is output)
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
; Return input status in A (0 = no char, != 0 char waiting)
|
|
;
|
|
cst:
|
|
; Save incoming registers (AF is output)
|
|
push bc
|
|
push de
|
|
push hl
|
|
;
|
|
; Get console input status via UBIOS
|
|
ld bc,$13
|
|
rst 08
|
|
ld a,e
|
|
or a
|
|
;
|
|
; Restore registers (AF is output)
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
#endif
|
|
;
|
|
; Generic console I/O
|
|
;
|
|
CIN .equ cin
|
|
COUT .equ cout
|
|
CST .equ cst
|
|
;
|
|
;=======================================================================
|
|
; Device inventory display
|
|
;=======================================================================
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
;
|
|
; Print list of all drives (WBW)
|
|
;
|
|
; Just invoke the existing HBIOS routine...
|
|
;
|
|
prtall:
|
|
ld a,BID_BIOS ; BIOS Bank please
|
|
ld ix,$0406 ; HBIOS PRTSUM vector
|
|
jp HB_BNKCALL ; do it
|
|
;
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
;
|
|
; Print list of all drives (UNA)
|
|
;
|
|
prtall:
|
|
ld hl,str_devlst ; device list header string
|
|
call pstr ; display it
|
|
call nl ; formatting
|
|
ld b,0 ; start with unit 0
|
|
;
|
|
prtall1: ; loop thru all units available
|
|
ld c,$48 ; UNA func: get disk type
|
|
ld l,0 ; preset unit count to zero
|
|
rst 08 ; call UNA, B preserved
|
|
ld a,l ; unit count to a
|
|
or a ; past end?
|
|
ret z ; we are done
|
|
push bc ; save unit
|
|
call prtdrv ; process the unit
|
|
pop bc ; restore unit
|
|
inc b ; next unit
|
|
jr prtall1 ; loop
|
|
;
|
|
; print the una unit info
|
|
; on input b has unit
|
|
;
|
|
prtdrv:
|
|
push bc ; save unit
|
|
push de ; save disk type
|
|
ld hl,str_disk ; prefix string
|
|
call pstr ; display it
|
|
ld a,b ; index
|
|
call prtdecb ; print it
|
|
ld a,' ' ; formatting
|
|
call cout ; do it
|
|
ld a,'=' ; formatting
|
|
call cout ; do it
|
|
ld a,' ' ; formatting
|
|
call cout ; do it
|
|
pop de ; recover disk type
|
|
ld a,d ; disk type to a
|
|
cp $40 ; ram/rom?
|
|
jr z,prtdrv1 ; handle ram/rom
|
|
ld hl,devide ; assume ide
|
|
cp $41 ; ide?
|
|
jr z,prtdrv2 ; print it
|
|
ld hl,devppide ; assume ppide
|
|
cp $42 ; ppide?
|
|
jr z,prtdrv2 ; print it
|
|
ld hl,devsd ; assume sd
|
|
cp $43 ; sd?
|
|
jr z,prtdrv2 ; print it
|
|
ld hl,devdsd ; assume dsd
|
|
cp $44 ; dsd?
|
|
jr z,prtdrv2 ; print it
|
|
ld hl,devunk ; otherwise unknown
|
|
jr prtdrv2
|
|
;
|
|
prtdrv1: ; handle ram/rom
|
|
ld c,$45 ; una func: get disk info
|
|
ld de,bl_infosec ; 512 byte buffer
|
|
rst 08 ; call una
|
|
bit 7,b ; test ram drive bit
|
|
ld hl,devrom ; assume rom
|
|
jr z,prtdrv2 ; if so, print it
|
|
ld hl,devram ; otherwise ram
|
|
jr prtdrv2 ; print it
|
|
;
|
|
prtdrv2: ; print device
|
|
pop bc ; recover unit
|
|
call pstr ; print device name
|
|
ld a,b ; unit to a
|
|
call prtdecb ; print it
|
|
ld a,':' ; device name suffix
|
|
call cout ; print it
|
|
ret ; done
|
|
;
|
|
devram .db "RAM",0
|
|
devrom .db "ROM",0
|
|
devide .db "IDE",0
|
|
devppide .db "PPIDE",0
|
|
devsd .db "SD",0
|
|
devdsd .db "DSD",0
|
|
devunk .db "UNK",0
|
|
;
|
|
str_devlst .db "\r\n\r\nDisk Devices:",0
|
|
;
|
|
#endif
|
|
|
|
#if (DSKYENABLE)
|
|
|
|
;
|
|
;=======================================================================
|
|
; DSKY interface routines
|
|
;=======================================================================
|
|
;
|
|
dsky_stat:
|
|
ld b,BF_DSKYSTAT
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_getkey:
|
|
ld b,BF_DSKYGETKEY
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_show:
|
|
ld b,BF_DSKYSHOWSEG
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_beep:
|
|
ld b,BF_DSKYBEEP
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_l2on:
|
|
ld e,1
|
|
jr dsky_statled
|
|
dsky_l2off:
|
|
ld e,0
|
|
dsky_statled:
|
|
ld b,BF_DSKYSTATLED
|
|
ld d,1
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_putled:
|
|
ld b,BF_DSKYKEYLEDS
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_highlightallkeys:
|
|
ld hl,dsky_highlightallkeyleds
|
|
jr dsky_putled
|
|
;
|
|
dsky_highlightkeysoff:
|
|
ld hl,dsky_highlightkeyledsoff
|
|
jr dsky_putled
|
|
;
|
|
#endif
|
|
;
|
|
dsky_msg:
|
|
#if (BIOS == BIOS_WBW)
|
|
ld b,BF_DSKYMESSAGE
|
|
jr dsky_hbcall
|
|
;
|
|
dsky_hbcall:
|
|
ld a,(dskyact)
|
|
or a
|
|
ret z
|
|
rst 08
|
|
#endif
|
|
ret
|
|
;
|
|
;=======================================================================
|
|
; Error handlers
|
|
;=======================================================================
|
|
;
|
|
err_invcmd:
|
|
ld hl,str_err_invcmd
|
|
jr err
|
|
;
|
|
err_nodisk:
|
|
ld hl,str_err_nodisk
|
|
jr err
|
|
;
|
|
err_nomedia:
|
|
ld hl,str_err_nomedia
|
|
jr err
|
|
;
|
|
err_noslice:
|
|
ld hl,str_err_noslice
|
|
jr err
|
|
;
|
|
err_badslice:
|
|
ld hl,str_err_badslice
|
|
jr err
|
|
err_nocon:
|
|
ld hl,str_err_nocon
|
|
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 a,(acmd_act) ; get auto cmd active flag
|
|
; or a ; set flags
|
|
; call nz,showcmd ; if auto cmd act, show cmd
|
|
; ld a,bel ; bel character
|
|
; call cout ; beep
|
|
ld hl,str_err_prefix
|
|
call pstr
|
|
pop hl
|
|
call pstr
|
|
or $ff ; signal error
|
|
ret ; done
|
|
;
|
|
str_err_prefix .db bel,"\r\n\r\n*** ",0
|
|
str_err_invcmd .db "Invalid command",0
|
|
str_err_nodisk .db "Disk unit not available",0
|
|
str_err_nomedia .db "Media not present",0
|
|
str_err_noslice .db "Disk unit does not support slices",0
|
|
str_err_badslice .db "Slice specified is illegal",0
|
|
str_err_nocon .db "Invalid character unit specification",0
|
|
str_err_diskio .db "Disk I/O failure",0
|
|
str_err_sig .db "No system image on disk",0
|
|
str_err_api .db "Unexpected hardware BIOS API failure",0
|
|
;
|
|
;=======================================================================
|
|
; Working data storage (initialized)
|
|
;=======================================================================
|
|
;
|
|
acmd .db BOOT_DEFAULT ; auto cmd string
|
|
.db 0
|
|
acmd_len .equ $ - acmd ; len of auto cmd
|
|
acmd_act .db $FF ; auto cmd active
|
|
acmd_to .dw BOOT_TIMEOUT ; auto cmd timeout
|
|
;
|
|
;=======================================================================
|
|
; Strings
|
|
;=======================================================================
|
|
;
|
|
str_banner .db PLATFORM_NAME," Boot Loader",0
|
|
str_appboot .db " (App Boot)",0
|
|
str_autoboot .db "AutoBoot: ",0
|
|
str_prompt .db "Boot [H=Help]: ",0
|
|
str_bs .db bs,' ',bs,0
|
|
str_reboot .db "\r\n\r\nRestarting System...",0
|
|
str_newcon .db "\r\n\r\n Console on Unit #",0
|
|
str_chspeed .db "\r\n\r\n Change speed now. Press a key to resume.",0
|
|
str_applst .db "\r\n\r\nROM Applications:",0
|
|
str_invcmd .db "\r\n\r\n*** Invalid Command ***",bel,0
|
|
str_load .db "\r\n\r\nLoading ",0
|
|
str_disk .db "\r\n Disk Unit ",0
|
|
str_on .db " on ",0
|
|
str_boot1 .db "\r\n\r\nBooting Disk Unit ",0
|
|
str_boot2 .db ", Slice ",0
|
|
str_binfo1 .db "\r\n\r\nVolume ",$22,0
|
|
str_binfo2 .db $22," [0x",0
|
|
str_binfo3 .db "-0x",0
|
|
str_binfo4 .db ", entry @ 0x",0
|
|
str_binfo5 .db "]",0
|
|
str_ldsec .db ", Sector 0x",0
|
|
str_diaglvl .db "\r\n\r\nHBIOS Diagnostic Level: ",0
|
|
;
|
|
str_help .db "\r\n"
|
|
.db "\r\n L - List ROM Applications"
|
|
.db "\r\n D - Device Inventory"
|
|
.db "\r\n R - Reboot System"
|
|
#if (BIOS == BIOS_WBW)
|
|
.db "\r\n I <u> [<c>] - Set Console Interface/Baud code"
|
|
.db "\r\n V [<n>] - View/Set HBIOS Diagnostic Verbosity"
|
|
#endif
|
|
.db "\r\n <u>[.<s>] - Boot Disk Unit/Slice"
|
|
.db 0
|
|
;;;;
|
|
;;;#if (DSKYENABLE)
|
|
;;; #if (GM7303ENABLE)
|
|
;;; ; The GM7303 has an ASCII LCD display
|
|
;;;msg_sel .db "Boot?", $00
|
|
;;;msg_boot .db "Boot...", $00
|
|
;;;msg_load .db "Load...", $00
|
|
;;;msg_go .db "Go...", $00
|
|
;;; #else
|
|
;;; ; Other DSKY devices use 7 segment LEDs
|
|
;;;msg_sel .db $7f,$5c,$5c,$78,$53,$00,$00,$00 ; "boot? "
|
|
;;;msg_boot .db $7f,$5c,$5c,$78,$80,$80,$80,$00 ; "boot... "
|
|
;;;msg_load .db $38,$5c,$5f,$5e,$80,$80,$80,$00 ; "load... "
|
|
;;;msg_go .db $3d,$5c,$80,$80,$80,$00,$00,$00 ; "go... "
|
|
;;; #endif
|
|
;;;#endif
|
|
;
|
|
;=======================================================================
|
|
; DSKY keypad led matrix masks
|
|
;=======================================================================
|
|
;
|
|
dsky_highlightallkeyleds .db $3f,$3f,$3f,$3f,$00,$00,$00,$00
|
|
dsky_highlightkeyledsoff .db $00,$00,$00,$00,$00,$00,$00,$00
|
|
;
|
|
;=======================================================================
|
|
; ROM Application Table
|
|
;=======================================================================
|
|
;
|
|
; Macro ra_ent:
|
|
;
|
|
; WBW UNA
|
|
; p1: Application name string adr word (+0) word (+0)
|
|
; p2: Console keyboard selection key byte (+2) byte (+2)
|
|
; p3: DSKY selection key byte (+3) byte (+3)
|
|
; p4: Application image bank byte (+4) word (+4)
|
|
; p5: Application image source address word (+5) word (+6)
|
|
; p6: Application image dest load address word (+7) word (+8)
|
|
; p7: Application image size word (+9) word (+10)
|
|
; p8: Application entry address word (+11) word (+12)
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
ra_name .equ 0
|
|
ra_conkey .equ 2
|
|
ra_dskykey .equ 3
|
|
ra_bnk .equ 4
|
|
ra_src .equ 5
|
|
ra_dest .equ 7
|
|
ra_siz .equ 9
|
|
ra_ent .equ 11
|
|
#endif
|
|
;
|
|
#if (BIOS == BIOS_UNA)
|
|
ra_name .equ 0
|
|
ra_conkey .equ 2
|
|
ra_dskykey .equ 3
|
|
ra_bnk .equ 4
|
|
ra_src .equ 6
|
|
ra_dest .equ 8
|
|
ra_siz .equ 10
|
|
ra_ent .equ 12
|
|
#endif
|
|
;
|
|
#define ra_ent(p1,p2,p3,p4,p5,p6,p7,p8) \
|
|
#defcont .dw p1 \
|
|
#defcont .db p2 \
|
|
#if (DSKYENABLE)
|
|
#defcont .db p3 \
|
|
#else
|
|
#defcont .db $FF \
|
|
#endif
|
|
#if (BIOS == BIOS_WBW)
|
|
#defcont .db p4 \
|
|
#endif
|
|
#if (BIOS == BIOS_UNA)
|
|
#defcont .dw p4 \
|
|
#endif
|
|
#defcont .dw p5 \
|
|
#defcont .dw p6 \
|
|
#defcont .dw p7 \
|
|
#defcont .dw p8
|
|
;
|
|
; Note: The formatting of the following is critical. TASM does not pass
|
|
; macro arguments well. Ensure std.asm holds the definitions for *_LOC,
|
|
; *_SIZ *_END and any code generated which does not include std.asm is
|
|
; synced.
|
|
;
|
|
; Note: The loadable ROM images are placed in ROM banks BID_IMG0 and
|
|
; BID_IMG1. However, RomWBW supports a mechanism to load a complete
|
|
; new system dynamically as a runnable application (see appboot
|
|
; in hbios.asm). In this case, the contents of BID_IMG0 will
|
|
; be pre-loaded into the currently executing ram bank thereby allowing
|
|
; those images to be dynamically loaded as well. To support this
|
|
; concept, a pseudo-bank called bid_cur is used to specify the images
|
|
; normally found in BID_IMG0. This special value will cause
|
|
; the associated image to be loaded from the currently executing bank
|
|
; which will be correct regardless of the load mode. Images in other
|
|
; banks (BID_IMG1) will always be loaded directly from ROM.
|
|
;
|
|
ra_tbl:
|
|
;
|
|
; Name Key Dsky Bank Src Dest Size Entry
|
|
; --------- ------- ----- -------- ----- ------- ------- ----------
|
|
ra_ent(str_mon, 'M', KY_CL, BID_IMG0, MON_IMGLOC, MON_LOC, MON_SIZ, MON_SERIAL)
|
|
ra_entsiz .equ $ - ra_tbl
|
|
#if (BIOS == BIOS_WBW)
|
|
#if (PLATFORM == PLT_S100)
|
|
ra_ent(str_smon, 'S', $FF, bid_cur , $8000, $8000, $0001, s100mon)
|
|
#endif
|
|
#endif
|
|
ra_ent(str_cpm22, 'C', KY_BK, BID_IMG0, CPM_IMGLOC, CPM_LOC, CPM_SIZ, CPM_ENT)
|
|
ra_ent(str_zsys, 'Z', KY_FW, BID_IMG0, ZSYS_IMGLOC, CPM_LOC, CPM_SIZ, CPM_ENT)
|
|
#if (BIOS == BIOS_WBW)
|
|
ra_ent(str_bas, 'B', KY_DE, BID_IMG1, BAS_IMGLOC, BAS_LOC, BAS_SIZ, BAS_LOC)
|
|
ra_ent(str_tbas, 'T', KY_EN, BID_IMG1, TBC_IMGLOC, TBC_LOC, TBC_SIZ, TBC_LOC)
|
|
ra_ent(str_fth, 'F', KY_EX, BID_IMG1, FTH_IMGLOC, FTH_LOC, FTH_SIZ, FTH_LOC)
|
|
ra_ent(str_play, 'P', $FF, BID_IMG1, GAM_IMGLOC, GAM_LOC, GAM_SIZ, GAM_LOC)
|
|
ra_ent(str_net, 'N', $FF, BID_IMG1, NET_IMGLOC, NET_LOC, NET_SIZ, NET_LOC)
|
|
ra_ent(str_upd, 'X', $FF, BID_IMG1, UPD_IMGLOC, UPD_LOC, UPD_SIZ, UPD_LOC)
|
|
ra_ent(str_user, 'U', $FF, BID_IMG1, USR_IMGLOC, USR_LOC, USR_SIZ, USR_LOC)
|
|
#endif
|
|
#if (DSKYENABLE)
|
|
ra_ent(str_dsky, 'Y'+$80, KY_GO, BID_IMG0, MON_IMGLOC, MON_LOC, MON_SIZ, MON_DSKY)
|
|
#endif
|
|
ra_ent(str_egg, 'E'+$80, $FF, BID_IMG1, EGG_IMGLOC, EGG_LOC, EGG_SIZ, EGG_LOC)
|
|
.dw 0 ; table terminator
|
|
;
|
|
ra_tbl_app:
|
|
;
|
|
; Name Key Dsky Bank Src Dest Size Entry
|
|
; --------- ------- ----- -------- ----- ------- ------- ----------
|
|
ra_ent(str_mon, 'M', KY_CL, bid_cur, MON_IMGLOC, MON_LOC, MON_SIZ, MON_SERIAL)
|
|
ra_ent(str_zsys, 'Z', KY_FW, bid_cur, ZSYS_IMGLOC, CPM_LOC, CPM_SIZ, CPM_ENT)
|
|
#if (DSKYENABLE)
|
|
ra_ent(str_dsky, 'Y'+$80, KY_GO, bid_cur, MON_IMGLOC, MON_LOC, MON_SIZ, MON_DSKY)
|
|
#endif
|
|
.dw 0 ; table terminator
|
|
;
|
|
str_mon .db "Monitor",0
|
|
str_cpm22 .db "CP/M 2.2",0
|
|
str_zsys .db "Z-System",0
|
|
str_dsky .db "DSKY Monitor",0
|
|
str_fth .db "Forth",0
|
|
str_bas .db "BASIC",0
|
|
str_tbas .db "Tasty BASIC",0
|
|
str_play .db "Play a Game",0
|
|
str_upd .db "XModem Flash Updater",0
|
|
str_user .db "User App",0
|
|
str_egg .db "",0
|
|
str_net .db "Network Boot",0
|
|
str_switches .db "FP Switches = 0x",0
|
|
newcon .db 0
|
|
newspeed .db 0
|
|
;
|
|
;=======================================================================
|
|
; Working data storage
|
|
;=======================================================================
|
|
;
|
|
.fill 64,0 ; 32 level stack
|
|
bl_stack .equ $ ; ... top is here
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
bid_ldr .db 0 ; bank at startup
|
|
#endif
|
|
#if (BIOS == BIOS_UNA)
|
|
bid_ldr .dw 0 ; bank at startup
|
|
#endif
|
|
;
|
|
lba .fill 4,0 ; lba for load, dword
|
|
dma .dw 0 ; address for load
|
|
sps .dw 0 ; sectors per slice
|
|
mediaid .db 0 ; media id
|
|
;
|
|
bootmode .db 0 ; ROM, APP, or IMG boot
|
|
ra_tbl_loc .dw 0 ; points to active ra_tbl
|
|
bootunit .db 0 ; boot disk unit
|
|
bootslice .db 0 ; boot disk slice
|
|
loadcnt .db 0 ; num disk sectors to load
|
|
switches .db 0 ; front panel switches
|
|
diskcnt .db 0 ; disk unit count value
|
|
dskyact .db 0 ; DSKY active if != 0
|
|
;
|
|
#if (BIOS == BIOS_WBW)
|
|
curcon .db CIO_CONSOLE ; current console unit
|
|
ciocnt .db 1 ; count of char units
|
|
savcon .db 0 ; con save for conpoll
|
|
conpend .db $ff ; pending con unit (first <space> pressed)
|
|
#endif
|
|
;
|
|
;=======================================================================
|
|
; Pad remainder of ROM Loader
|
|
;=======================================================================
|
|
;
|
|
slack .equ ($8000 + LDR_SIZ - $)
|
|
.fill slack
|
|
;
|
|
.echo "LOADER space remaining: "
|
|
.echo slack
|
|
.echo " bytes.\n"
|
|
;
|
|
;
|
|
;=======================================================================
|
|
; Disk buffers (uninitialized)
|
|
;=======================================================================
|
|
;
|
|
; Master Boot Record sector is read into area below.
|
|
; Note that this buffer is actually shared with bl_infosec
|
|
; buffer below.
|
|
;
|
|
bl_mbrsec .equ $
|
|
;
|
|
; Boot info sector is read into area below.
|
|
; The third sector of a disk device is reserved for boot info.
|
|
;
|
|
bl_infosec .equ $
|
|
.ds (512 - 128)
|
|
bb_metabuf .equ $
|
|
bb_sig .ds 2 ; signature (0xA55A if set)
|
|
bb_platform .ds 1 ; formatting platform
|
|
bb_device .ds 1 ; formatting device
|
|
bb_formatter .ds 8 ; formatting program
|
|
bb_drive .ds 1 ; physical disk drive #
|
|
bb_lu .ds 1 ; logical unit (lu)
|
|
.ds 1 ; msb of lu, now deprecated
|
|
.ds (bb_metabuf + 128) - $ - 32
|
|
bb_protect .ds 1 ; write protect boolean
|
|
bb_updates .ds 2 ; update counter
|
|
bb_rmj .ds 1 ; rmj major version number
|
|
bb_rmn .ds 1 ; rmn minor version number
|
|
bb_rup .ds 1 ; rup update number
|
|
bb_rtp .ds 1 ; rtp patch level
|
|
bb_label .ds 16 ; 16 character drive label
|
|
bb_term .ds 1 ; label terminator ('$')
|
|
bb_biloc .ds 2 ; loc to patch boot drive info
|
|
bb_cpmloc .ds 2 ; final ram dest for cpm/cbios
|
|
bb_cpmend .ds 2 ; end address for load
|
|
bb_cpment .ds 2 ; CP/M entry point (cbios boot)
|
|
;
|
|
.end
|