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