title 'general utility routines'
; i/o port init routines
public ioiniml,ioini1l ;
; math
public ijphl ; vectored CALL point
public add_hla ; add a to hl
public div32_16,div32_r ; divide 32 bit by 16 bit number (rounded)
; print utils
public ?pmsg ; print message
public pr.idx ; print message from table indexed by
public pr.inln,pr.crlf ; print message inline, print newline
public phex2,phex4 ; print 2 digit hex or 4 digit hex
public pr.dec,pr.decl ; print 16 or 32 bit decimal number
public pr.errors ; print BIOS disk error message header
extrn ?const,?conin,?cono
extrn @adrv,@trk,@sect ; used by disk error message
extrn @op,@ermde
;-------------------------------------------------------------------------------
cr equ 13
lf equ 10
bell equ 7
dseg
;----------------------------------------------------------------------
; output bytes to consecutive portaddresses
;
; hl: table with following structure:
; db n, port1, val1, val2,... valn
; db m, port1, val1, val2,... valm
; ...
; db 0 ; Terminate table
ioiniml:
push bc
xor a
ioml_lp:
ld b,(hl)
inc hl
cp b
jr z,ioml_e
ld c,(hl)
inc hl
otimr
jr ioml_lp
ioml_e:
pop bc
ret
;----------------------------------------------------------------------
; output bytes to ports
;
; hl: tables of port,value pairs:
; db n, port1,val1, port2,val2,... portn,valn
; ...
; db 0 ; Terminate table
ioini1l:
push bc
jr io1_nxt
io1_lp:
ld c,(hl) ;port address
inc hl
otim
jr nz,io1_lp
io1_nxt:
ld b,(hl) ;count
inc hl
inc b
djnz io1_lp
pop bc
ret
cseg
;--------------------------------------------------------------------
; vectored CALL point
ijphl:
jp (hl)
dseg
;--------------------------------------------------------------------
; add a to hl
;
; return:
; hl = hl + a
; Flags undefined
add_hla:
add a,l
ld l,a
ret nc
inc h
ret
;--------------------------------------------------------------------
; rounded div 32 by 16 bit
;
; DEHL: Dividend (x)
; BC: Divisor (y)
; return:
; HLDE: Rounded Quotient (q)
; BC: Remainder (r)
div32_r:
push bc
srl b ;y/2
rr c
add hl,bc ;low x + y/2
pop bc
jr nc,div_r1
inc de
div_r1:
;fall thru
;--------------------------------------------------------------------
; Divide 32 bit by 16
;
; DEHL: Dividend (x)
; BC: Divisor (y)
;
; return:
; DEHL: Quotient
; BC: Reminder
div32_16:
exx ;low
push de ;save alternate registers (de,bc)
push bc
exx ;high
push hl ;lx
push bc ;ly
ld bc,0 ;bc = hy = 0
ld h,b ;hl = hr = 0
ld l,c
;de = x, hl = r
exx ;low
pop bc ;bc' = ly
ex (sp),hl ;hl' = lx, save alternate hl
ld de,0 ;de' = lr = 0
ex de,hl ;de = x, hl = r
exx ;high
ld a,32 ;count
;
; start:
; de: x (de: hx, de': lx)
; bc: y (bc: hy, bc': ly)
; hl: 0
;
div_lp: ;do
exx ; low
ex de,hl ; x
add hl,hl ; x <<= 1
exx ; high
ex de,hl ; x
adc hl,hl ; x <<= 1
exx ; low
ex de,hl ; r
adc hl,hl ; r <<= 1
exx ; high
ex de,hl ; r
adc hl,hl ; r <<= 1
exx ; low
inc de ; x/q += 1
or a ;
sbc hl,bc ;
exx ; high
sbc hl,bc ;
jr nc,div_no_restore
exx ; low
dec de ;
add hl,bc ; r += y
exx ; high
adc hl,bc ;
div_no_restore: ;
dec a ;
jr nz,div_lp ;while (--count)
; result:
; de: q (de: hq, de': lq)
; hl: r (hl: hr, hl': lr)
exx ;low
ex de,hl ;hl = lq, de = lr
ex (sp),hl ;lq
push de ;lr
exx ;high
pop bc ;bc = lr
pop hl ;de = lq
exx ;low
pop bc ;restore alternate registers
pop de
exx ;high
ret
;-------------------------------------------------------------------------------
; print message @ up to a null
; saves &
?pmsg:
push bc
push de
pmsg$loop:
ld a,(hl)
inc hl
or a
jr z,pmsg$exit
ld c,a
push hl
call ?cono
pop hl
jr pmsg$loop
pmsg$exit:
pop de
pop bc
ret
;-------------------------------------------------------------------------------
; print message from table @, indexed by
; saves &
pr.idx:
push bc
push de
push hl ; put pointer to first message on stack
ld e,a ; save message number
xor a
ld b,a
ld c,a
inc e
pdc_nxt_str:
dec e
ex (sp),hl
jr z,pdc_found
ex (sp),hl
cpir
cp (hl)
jr nz,pdc_nxt_str
; End of List, msg not found.
; Print first msg.
pdc_found:
pop hl
call ?pmsg
pop de
pop bc
ret
;-------------------------------------------------------------------------------
; print message inline up to a null
; saves all registers
pr.inln:
ex (sp),hl
push af
call ?pmsg
pop af
ex (sp),hl
ret
;-------------------------------------------------------------------------------
; print
; saves all registers
pr.crlf:
call pr.inln
db cr,lf,0
ret
;-------------------------------------------------------------------------------
; print hl as a 4 digit hexadecimal number
; saves all registers
phex4:
ld a,h
call phex2
ld a,l
; fall thru
;-------------------------------------------------------------------------------
; print a as a 2 digit hexadecimal number
; saves all registers
phex2:
push af
rra
rra
rra
rra
call print.digit
pop af
print.digit:
push hl
push de
push bc
push af
and 00fh
cp 10
jr c,prd_1
add a,007h
prd_1:
add a,'0'
ld c,a
call ?cono
pop af
pop bc
pop de
pop hl
ret
;-------------------------------------------------------------------------------
; print decimal 16 bit number from HL
;
; HL: unsigned binary number to print
; C: minimum print field width
; number is prined right-aligned
; B: pad character, typically ' ' or '0'
pr.dec:
push de
ld de,0
call pr.decl
pop de
ret
;-------------------------------------------------------------------------------
; print decimal 32 bit number from DEHL
;
; DEHL: unsigned binary number to print
; C: minimum print field width
; number is prined right-aligned
; B: pad character, typically ' ' or '0'
pr.decl:
push bc ;save width and fillchar
push bc
exx ;(alt)
ex (sp),hl ;save hl', get width and fill
push de ;save de'
xor a
ld d,a ;clear counter
ld e,a
push af ; string terminator
inc sp
prd_divloop: ;do
exx ; (main)
ld bc,10 ;
call div32_16 ; get a digit
ld a,c ;
add a,'0' ; make it printable
push af ;
ld a,h ;
or l ;
or d ;
or e ;
exx ; (alt)
inc sp ;
inc de ;
jr nz,prd_divloop ;
prd_filloop: ;h=filler, l=field width
ld a,e
cp l
jr nc,prd_out
push hl
inc sp
inc de
jr prd_filloop
prd_out:
ld hl,0
add hl,sp ;ptr to beginning of number string (hl==0 here)
call ?pmsg
ex de,hl
add hl,sp
ld sp,hl
inc sp ;remove string terminator
pop de
pop hl
exx ;(main)
pop bc
ret
;-------------------------------------------------------------------------------
?pderr:
ld hl,drive$msg
call ?pmsg ; error header
ld a,(@adrv)
add a,'A'
ld c,a
call ?cono ; drive code
ld hl,track$msg
call ?pmsg ; track header
ld c,0
ld hl,(@trk)
call pr.dec ; track number
ld hl,sector$msg
call ?pmsg ; sector header
ld hl,(@sect)
jp pr.dec ; sector number
; error message components
drive$msg: db cr,lf,bell,'BIOS Error on ',0
track$msg: db ': T-',0
sector$msg: db ', S-',0
;-------------------------------------------------------------------------------
; get console input, echo it, and shift to upper case
; save hl,de,bc
uciecho:
push hl
push de
push bc
u$c0:
call ?const
or a
jr z,u$c1 ; see if any char already struck
call ?conin
jr u$c0 ; yes, eat it and try again
u$c1:
call ?conin
push af
ld c,a
cp ' '-1
call nc,?cono
pop af
pop bc
pop de
pop hl
cp 'a'
ret c
sub 'a'-'A' ; make upper case
ret
;-------------------------------------------------------------------------------
;
pr.errors:
; suppress error message if BDOS
; is returning errors to application...
ld a,(@ermde)
inc a
jr nz,pre1
dec a ;return NZ, if @ermde == 0FFH
ret
pre1:
push hl
ld hl,pre2
ex (sp),hl
push hl
; Had permanent error, print message like:
; BIOS Err on d: T-nn, S-mm, , Retry ?
call ?pderr ; print message header
ld hl,op$msg
ld a,(@op)
jp pr.idx ; last function (read or write)
pre2:
; prompt for retry
call pr.inln
db ' Retry (Y/N) ? ',0
call uciecho ; get operator response
cp 'Y'
ret ; return Z-flag for yes
op$msg:
db ', Unknown op, ',0
db ', Read, ',0
db ', Write, ',0
db 0
end