- title 'general utility routines'
-
- ; i/o port init routines
-
- public ioiniml,ioini1l ;
-
- ; math
-
- 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.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 ?pderr ; print BIOS disk error message header
-
-
-
- extrn ?cono
- extrn @adrv,@trk,@sect ; used by disk error message
-
-;-------------------------------------------------------------------------------
-
-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
-
-;--------------------------------------------------------------------
-; 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 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)
- call pr.dec ; sector number
- ret
-
- ; error message components
-drive$msg: db cr,lf,bell,'BIOS Error on ',0
-track$msg: db ': T-',0
-sector$msg: db ', S-',0
+ title 'general utility routines'\r
+\r
+ ; i/o port init routines\r
+\r
+ public ioiniml,ioini1l ;\r
+\r
+ ; math\r
+\r
+ public ijphl ; vectored CALL point\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 ?pchar,?pmsg ; print char <A>, print message (<HL>)\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
+\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
+ cseg\r
+\r
+;--------------------------------------------------------------------\r
+; vectored CALL point\r
+\r
+ijphl:\r
+ jp (hl)\r
+\r
+ dseg\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 from table @<HL>, indexed by <A>\r
+; saves <BC> & <DE>\r
+\r
+pr.idx:\r
+ push bc\r
+ push hl ; put pointer to first message on stack\r
+ ld b,a ; save message number\r
+ or a\r
+ jr z,pdc_done\r
+ xor a\r
+pdc_skip:\r
+ bit 7,(hl)\r
+ inc hl\r
+ jr z,pdc_skip\r
+ cp (hl)\r
+ jr z,pdc_done ; End of List, msg not found. Print first msg.\r
+ djnz pdc_skip\r
+ ex (sp),hl ; Remove first msg from stack\r
+pdc_done:\r
+ pop hl ; Get msg. to print\r
+ pop bc\r
+ ;fall thru\r
+\r
+;-------------------------------------------------------------------------------\r
+; print message @<HL> up to a null\r
+; saves <BC> & <DE>\r
+\r
+?pmsg:\r
+ ld a,(hl)\r
+ inc hl\r
+ and 07fh\r
+ ret z\r
+ call ?pchar\r
+ dec hl\r
+ bit 7,(hl)\r
+ inc hl\r
+ jr z,?pmsg\r
+ ret\r
+\r
+;-------------------------------------------------------------------------------\r
+; print message inline up to a null\r
+; saves <BC>, <DE>, <HL>\r
+\r
+pr.inln:\r
+ ex (sp),hl\r
+ call ?pmsg\r
+ ex (sp),hl\r
+ ret\r
+\r
+;-------------------------------------------------------------------------------\r
+; print <CR><LF>\r
+; saves <BC>, <DE>, <HL>\r
+\r
+pr.crlf:\r
+ call pr.inln\r
+ dc cr,lf\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
+ daa\r
+ add a,0a0h\r
+ adc a,040h\r
+ ;fall thru\r
+\r
+;-------------------------------------------------------------------------------\r
+; print character in a\r
+; saves <BC>, <DE>, <HL>\r
+\r
+?pchar:\r
+ push bc\r
+ push de\r
+ push hl\r
+ ld c,a\r
+ call ?cono\r
+ pop hl\r
+ pop de\r
+ pop bc\r
+ ret\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 printed 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 printed 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 a,(@adrv)\r
+ add a,'A'\r
+ ld (msg_drv),a\r
+ call pr.inln ; error header\r
+ db cr,lf,bell,'BIOS Error on '\r
+msg_drv:\r
+ db 'A'\r
+ dc ': T-'\r
+ ld c,0\r
+ ld hl,(@trk)\r
+ call pr.dec ; track number\r
+ call pr.inln ; sector header\r
+ dc ', S-'\r
+ ld hl,(@sect)\r
+ jp pr.dec ; sector number\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
+ dc ' Retry (Y/N) ? '\r
+\r
+ call uciecho ; get operator response\r
+ cp 'Y'\r
+ ret ; return Z-flag for yes\r
+\r
+\r
+op$msg:\r
+ dc ', Unknown op, '\r
+ dc ', Read, '\r
+ dc ', Write, '\r
+ db 0\r
+\r
+ end\r