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 ; ; HLDE: 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) or a jr z,pmsg$exit ld c,a push hl call ?cono pop hl inc 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 ; 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