From 31dbbc3fe152e4e9d44a5e0e5d72998203dba8cc Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 10 Oct 2026 15:41:32 +0200 Subject: [PATCH] m68k: implement bcd library routines --- compiler/res/prog8lib/qemu68k/bcd.p8 | 2 +- compiler/res/prog8lib/shared_amiga_bcd.p8 | 132 ++++++++++++++++++++- docs/source/libraries.rst | 19 ++-- examples/test.p8 | 133 +++++++++++++++++++++- 4 files changed, 274 insertions(+), 12 deletions(-) diff --git a/compiler/res/prog8lib/qemu68k/bcd.p8 b/compiler/res/prog8lib/qemu68k/bcd.p8 index 79f43c39b..2747921ea 100644 --- a/compiler/res/prog8lib/qemu68k/bcd.p8 +++ b/compiler/res/prog8lib/qemu68k/bcd.p8 @@ -1 +1 @@ -; no support for BCD +%import shared_amiga_bcd diff --git a/compiler/res/prog8lib/shared_amiga_bcd.p8 b/compiler/res/prog8lib/shared_amiga_bcd.p8 index 79f43c39b..f208c1188 100644 --- a/compiler/res/prog8lib/shared_amiga_bcd.p8 +++ b/compiler/res/prog8lib/shared_amiga_bcd.p8 @@ -1 +1,131 @@ -; no support for BCD +bcd { + %option merge, ignore_unused + + asmsub addb(byte a @D0, byte b @D1) clobbers(D1) -> byte @D0 { + %asm {{ + andi.b #$ef,ccr + abcd d1,d0 + rts + }} + } + + asmsub addub(ubyte a @D0, ubyte b @D1) clobbers(D1) -> ubyte @D0 { + %asm {{ + andi.b #$ef,ccr + abcd d1,d0 + rts + }} + } + + asmsub addw(word a @D0, word b @D1) clobbers(D1) -> word @D0 { + %asm {{ + andi.b #$ef,ccr + abcd d1,d0 + ror.w #8,d0 + ror.w #8,d1 + abcd d1,d0 + ror.w #8,d0 + rts + }} + } + + asmsub adduw(uword a @D0, uword b @D1) clobbers(D1) -> uword @D0 { + %asm {{ + andi.b #$ef,ccr + abcd d1,d0 + ror.w #8,d0 + ror.w #8,d1 + abcd d1,d0 + ror.w #8,d0 + rts + }} + } + + asmsub addl(long a @D0, long b @D1) clobbers(D1) -> long @D0 { + %asm {{ + andi.b #$ef,ccr + abcd d1,d0 + rept 3 + ror.l #8,d0 + ror.l #8,d1 + abcd d1,d0 + endr + ror.l #8,d0 + rts + }} + } + + asmsub subb(byte a @D0, byte b @D1) clobbers(D1) -> byte @D0 { + %asm {{ + andi.b #$ef,ccr + sbcd d1,d0 + rts + }} + } + + asmsub subub(ubyte a @D0, ubyte b @D1) clobbers(D1) -> ubyte @D0 { + %asm {{ + andi.b #$ef,ccr + sbcd d1,d0 + rts + }} + } + + asmsub subuw(uword a @D0, uword b @D1) clobbers(D1) -> uword @D0 { + %asm {{ + andi.b #$ef,ccr + sbcd d1,d0 + ror.w #8,d0 + ror.w #8,d1 + sbcd d1,d0 + ror.w #8,d0 + rts + }} + } + + asmsub subl(long a @D0, long b @D1) clobbers(D1) -> long @D0 { + %asm {{ + andi.b #$ef,ccr + sbcd d1,d0 + rept 3 + ror.l #8,d0 + ror.l #8,d1 + sbcd d1,d0 + endr + ror.l #8,d0 + rts + }} + } + + asmsub addtol(^^long a @A0, long b @D0) clobbers(D0,D1) { + %asm {{ + move.l (a0),d1 + andi.b #$ef,ccr + abcd d0,d1 + rept 3 + ror.l #8,d0 + ror.l #8,d1 + abcd d0,d1 + endr + ror.l #8,d1 + move.l d1,(a0) + rts + }} + } + + asmsub subfroml(^^long a @A0, long b @D0) clobbers(D0,D1) { + %asm {{ + move.l (a0),d1 + andi.b #$ef,ccr + sbcd d0,d1 + rept 3 + ror.l #8,d0 + ror.l #8,d1 + sbcd d0,d1 + endr + ror.l #8,d1 + move.l d1,(a0) + rts + }} + } +} diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 56cd584d1..c336359ff 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -577,23 +577,28 @@ bcd ^^^ .. index:: pair: Libraries; bcd -Decimal addition and subtraction routines, so for example $0987 + $1111 = $2098 (rather than the usual hex outcome $1a98) -Utilizes the BCD mode of the CPU (note: not all 6502 variants support this mode). -This mode is useful for example for counting decimal score in a game, to avoid costly conversion to a decimal display string: -just print the hexadecimal score representation. (This gets especially noticeable with long integers) -Available routines: +Decimal addition and subtraction routines using packed BCD values. For example, +``$0987 + $1111`` produces ``$2098`` rather than the usual binary result +``$1a98``. A BCD value can be printed directly as hexadecimal because each +hexadecimal digit represents one decimal digit. + +The library uses the CPU's BCD instructions. It is not available on the ``virtual`` target. + +Available value-returning routines: - ``sub addb(byte a, byte b) -> byte`` - ``sub addub(ubyte a, ubyte b) -> ubyte`` - ``sub addw(word a, word b) -> word`` - ``sub adduw(uword a, uword b) -> uword`` - ``sub addl(long a, long b) -> long`` -- ``sub addtol(^^long a, long b)`` (adds b in-place to a, saves copying values) +- ``sub addtol(^^long a, long b)`` (adds ``b`` in-place to ``a``) - ``sub subb(byte a, byte b) -> byte`` - ``sub subub(ubyte a, ubyte b) -> ubyte`` - ``sub subuw(uword a, uword b) -> uword`` - ``sub subl(long a, long b) -> long`` -- ``sub subfroml(^^long a, long b)`` (subtracts b in-place from a, saves copying values) +- ``sub subfroml(^^long a, long b)`` (subtracts ``b`` in-place from ``a``) + +The in-place long routines try to avoid copying values. bmx (cx16 only) diff --git a/examples/test.p8 b/examples/test.p8 index 76e5e4f88..c14033486 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,8 +1,135 @@ +%encoding iso %import textio +%import bcd +%zeropage basicsafe + main { sub start() { - sys.die($99ea, "ik ga dood") - txt.print_ub(123) - txt.print("\nderp\n") + txt.iso() + + txt.print("bcd addition (hex: each nibble is a decimal digit)\n") + check_byte_add($25, $13, $38) + check_byte_add($58, $41, $99) + + txt.print("\nbcd subtraction\n") + check_byte_sub($64, $27, $37) + check_byte_sub($80, $29, $51) + + txt.print("\nbcd word addition\n") + check_word_add($1234, $5678, $6912) + + txt.print("\nbcd word subtraction\n") + check_word_sub($8000, $1234, $6766) + + txt.print("\nbcd long addition\n") + check_long_add($12345678, $87654321, $99999999) + + txt.print("\nbcd long subtraction\n") + check_long_sub($98765432, $12345678, $86419754) + + txt.print("\nbcd long in-place addition\n") + check_long_add_in_place($12345678, $87654321, $99999999) + + txt.print("\nbcd long in-place subtraction\n") + check_long_sub_in_place($98765432, $12345678, $86419754) + + ;sys.poweroff_system() + } + + sub check_byte_add(ubyte a, ubyte b, ubyte expected) { + ubyte result = bcd.addub(a, b) + txt.print_ubhex(a, false) + txt.print(" + ") + txt.print_ubhex(b, false) + txt.print(" = ") + txt.print_ubhex(result, false) + txt.print(" expected ") + txt.print_ubhex(expected, false) + print_verdict(result == expected) + } + + sub check_byte_sub(ubyte a, ubyte b, ubyte expected) { + ubyte result = bcd.subub(a, b) + txt.print_ubhex(a, false) + txt.print(" - ") + txt.print_ubhex(b, false) + txt.print(" = ") + txt.print_ubhex(result, false) + txt.print(" expected ") + txt.print_ubhex(expected, false) + print_verdict(result == expected) + } + + sub check_word_add(uword a, uword b, uword expected) { + uword result = bcd.adduw(a, b) + txt.print_uwhex(a, false) + txt.print(" + ") + txt.print_uwhex(b, false) + txt.print(" = ") + txt.print_uwhex(result, false) + txt.print(" expected ") + txt.print_uwhex(expected, false) + print_verdict(result == expected) + } + + sub check_word_sub(uword a, uword b, uword expected) { + uword result = bcd.subuw(a, b) + txt.print_uwhex(a, false) + txt.print(" - ") + txt.print_uwhex(b, false) + txt.print(" = ") + txt.print_uwhex(result, false) + txt.print(" expected ") + txt.print_uwhex(expected, false) + print_verdict(result == expected) + } + + sub check_long_add(long a, long b, long expected) { + long result = bcd.addl(a, b) + txt.print_ulhex(a, false) + txt.print(" + ") + txt.print_ulhex(b, false) + txt.print(" = ") + txt.print_ulhex(result, false) + txt.print(" expected ") + txt.print_ulhex(expected, false) + print_verdict(result == expected) + } + + sub check_long_sub(long a, long b, long expected) { + long result = bcd.subl(a, b) + txt.print_ulhex(a, false) + txt.print(" - ") + txt.print_ulhex(b, false) + txt.print(" = ") + txt.print_ulhex(result, false) + txt.print(" expected ") + txt.print_ulhex(expected, false) + print_verdict(result == expected) + } + + sub check_long_add_in_place(long initial, long value, long expected) { + long @shared result = initial + bcd.addtol(&result, value) + txt.print_ulhex(result, false) + txt.print(" expected ") + txt.print_ulhex(expected, false) + print_verdict(result == expected) + } + + sub check_long_sub_in_place(long initial, long value, long expected) { + long @shared result = initial + bcd.subfroml(&result, value) + txt.print_ulhex(result, false) + txt.print(" expected ") + txt.print_ulhex(expected, false) + print_verdict(result == expected) + } + + sub print_verdict(bool ok) { + if ok + txt.print(" ok\n") + else + txt.print(" fail\n") } }