From 1c65612935a225cf47fd29d44b2ac66ba9cae072 Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Mon, 15 Sep 2025 21:37:38 -0600 Subject: [PATCH 1/2] implemented i48div --- src/libc/i48div.src | 28 +++- test/standalone/i48div/autotest.json | 40 ++++++ test/standalone/i48div/makefile | 19 +++ test/standalone/i48div/src/main.c | 193 +++++++++++++++++++++++++++ 4 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 test/standalone/i48div/autotest.json create mode 100644 test/standalone/i48div/makefile create mode 100644 test/standalone/i48div/src/main.c diff --git a/src/libc/i48div.src b/src/libc/i48div.src index 53b80bcdf..a511137a3 100644 --- a/src/libc/i48div.src +++ b/src/libc/i48div.src @@ -2,7 +2,31 @@ section .text -; public _i48div + public _i48div ; i48div_t i48div(int48_t numer, int48_t denom); -; _i48div: +_i48div: + ; TODO: __i48dvrmu is called twice here + push ix + ld ix, 0 + add ix, sp + ld hl, (ix + 9) + ld de, (ix + 12) + ld bc, (ix + 15) + ld iy, (ix + 18) + ld ix, (ix + 6) + push hl + push de + call __i48divs + ld (ix + 0), hl + ld (ix + 3), de + pop de + pop hl + call __i48rems + ld (ix + 6), hl + ld (ix + 9), de + pop ix + ret + + extern __i48divs + extern __i48rems diff --git a/test/standalone/i48div/autotest.json b/test/standalone/i48div/autotest.json new file mode 100644 index 000000000..be5eeed3c --- /dev/null +++ b/test/standalone/i48div/autotest.json @@ -0,0 +1,40 @@ +{ + "transfer_files": [ + "bin/DEMO.8xp" + ], + "target": { + "name": "DEMO", + "isASM": true + }, + "sequence": [ + "action|launch", + "delay|1000", + "hashWait|1", + "key|enter", + "delay|300", + "hashWait|2" + ], + "hashes": { + "1": { + "description": "All tests passed", + "timeout": 5000, + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "38E2AD5A" + ] + }, + "2": { + "description": "Exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "FFAF89BA", + "101734A5", + "9DA19F44", + "A32840C8", + "349F4775" + ] + } + } +} diff --git a/test/standalone/i48div/makefile b/test/standalone/i48div/makefile new file mode 100644 index 000000000..41673220d --- /dev/null +++ b/test/standalone/i48div/makefile @@ -0,0 +1,19 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO +ARCHIVED = NO + +CFLAGS = -Wall -Wextra -Wshadow -Wconversion -Wformat=2 -Wno-sign-conversion -Oz +CXXFLAGS = -Wall -Wextra -Wshadow -Wconversion -Wformat=2 -Wno-sign-conversion -Oz + +PREFER_OS_LIBC = NO +PREFER_OS_CRT = NO + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/standalone/i48div/src/main.c b/test/standalone/i48div/src/main.c new file mode 100644 index 000000000..32c5203be --- /dev/null +++ b/test/standalone/i48div/src/main.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Config +//------------------------------------------------------------------------------ + +#define RANDOM_TEST_COUNT 256 + +// define to 0 or 1 +#define DEBUG_DIAGNOSTICS 0 + +#define AUTOTEST_SEED 0x7184CE + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +#define C(expr) if (!(expr)) { return __LINE__; } + +#define TEST(test) { ret = test; if (ret != 0) { return ret; }} + +#ifndef DEBUG_DIAGNOSTICS +#error "DEBUG_DIAGNOSTICS needs to be defined to 0 or 1" +#endif + +#if RANDOM_TEST_COUNT < 4 +#error "RANDOM_TEST_COUNT is out of range" +#endif + +#if DEBUG_DIAGNOSTICS +#define test_printf printf +#else +#define test_printf(...) +#endif + +static_assert(RAND_MAX == INT_MAX, "RAND_MAX has changed"); + +#define rand8() ((int8_t)rand()) + +#define rand16() ((int16_t)rand()) + +__attribute__((__unused__)) static int24_t rand24(void) { + union { + int24_t u24; + struct { + uint16_t lo16; + uint8_t hi8; + } part; + } split; + split.part.lo16 = (uint16_t)rand(); + split.part.hi8 = (uint8_t)rand(); + return split.u24; +} + +__attribute__((__unused__)) static int32_t rand32(void) { + union { + int32_t u32; + uint16_t u16[2]; + } split; + split.u16[0] = (uint16_t)rand(); + split.u16[1] = (uint16_t)rand(); + return split.u32; +} + +__attribute__((__unused__)) static int48_t rand48(void) { + union { + int48_t u48; + uint16_t u16[3]; + } split; + split.u16[0] = (uint16_t)rand(); + split.u16[1] = (uint16_t)rand(); + split.u16[2] = (uint16_t)rand(); + return split.u48; +} + +__attribute__((__unused__)) static int64_t rand64(void) { + union { + int64_t u64; + uint16_t u16[4]; + } split; + split.u16[0] = (uint16_t)rand(); + split.u16[1] = (uint16_t)rand(); + split.u16[2] = (uint16_t)rand(); + split.u16[3] = (uint16_t)rand(); + return split.u64; +} + +static bool test_i48div(int48_t x, int48_t y) { + if (y == 0) { + // division by zero + return true; + } + lldiv_t truth = lldiv((int64_t)x, (int64_t)y); + i48div_t guess = i48div(x, y); + if ( + ((uint48_t)truth.quot != (uint48_t)guess.quot) || + ((uint48_t)truth.rem != (uint48_t)guess.rem) + ) { + test_printf( + "XN: %012llX\nYD: %012llX\n"\ + "GQ: %012llX\nGR: %012llX\n"\ + "TQ: %012llX\nTR: %012llX\n", + (uint64_t)(uint48_t)x , (uint64_t)(uint48_t)y, + (uint64_t)(uint48_t)guess.quot, (uint64_t)(uint48_t)guess.rem, + (uint64_t)(uint48_t)truth.quot, (uint64_t)(uint48_t)truth.rem + ); + return false; + } + return true; +} + +int run_tests(void) { + srand(AUTOTEST_SEED); + + C(test_i48div( 0, 1)); + C(test_i48div( 1, 1)); + C(test_i48div( -1, 1)); + C(test_i48div( INT48_MAX, 1)); + C(test_i48div(-INT48_MAX, 1)); + C(test_i48div( INT48_MIN, 1)); + C(test_i48div( 0, -1)); + C(test_i48div( 1, -1)); + C(test_i48div( -1, -1)); + C(test_i48div( INT48_MAX, -1)); + C(test_i48div(-INT48_MAX, -1)); + C(test_i48div( INT48_MIN, -1)); + C(test_i48div( 0, INT48_MAX)); + C(test_i48div( 1, INT48_MAX)); + C(test_i48div( -1, INT48_MAX)); + C(test_i48div( 0, -INT48_MAX)); + C(test_i48div( 1, -INT48_MAX)); + C(test_i48div( -1, -INT48_MAX)); + C(test_i48div( 0, INT48_MIN)); + C(test_i48div( 1, INT48_MIN)); + C(test_i48div( -1, INT48_MIN)); + C(test_i48div( INT48_MAX, INT48_MAX)); + C(test_i48div( INT48_MAX, -INT48_MAX)); + C(test_i48div( INT48_MAX, INT48_MIN)); + C(test_i48div(-INT48_MAX, INT48_MAX)); + C(test_i48div(-INT48_MAX, -INT48_MAX)); + C(test_i48div(-INT48_MAX, INT48_MIN)); + C(test_i48div( INT48_MIN, INT48_MAX)); + C(test_i48div( INT48_MIN, -INT48_MAX)); + C(test_i48div( INT48_MIN, INT48_MIN)); + + for (int i = 0; i < RANDOM_TEST_COUNT; i++) { + if (!test_i48div(rand48(), rand48())) { + return __LINE__; + } + } + + for (int i = 0; i < RANDOM_TEST_COUNT; i++) { + if (!test_i48div(rand48(), rand24())) { + return __LINE__; + } + } + + for (int i = 0; i < RANDOM_TEST_COUNT; i++) { + if (!test_i48div(rand48(), rand8())) { + return __LINE__; + } + } + + return 0; +} + +int main(void) { + os_ClrHome(); + int failed_test = run_tests(); + if (failed_test != 0) { + char buf[sizeof("Failed test L-8388608\n")]; + boot_sprintf(buf, "Failed test L%d\n", failed_test); + fputs(buf, stdout); + } else { + fputs("All tests passed", stdout); + } + + while (!os_GetCSC()); + + return 0; +} From 6669c778b9d72ccfbddc10bad69688d7f7feb7eb Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:45:11 -0600 Subject: [PATCH 2/2] optimized i48div and tail called negation in signed division routines --- src/crt/i48rems.src | 7 ++++--- src/crt/lldivs.src | 5 ++--- src/libc/i48div.src | 32 +++++++++++++++++++++----------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/crt/i48rems.src b/src/crt/i48rems.src index fa784191f..3e44c2081 100644 --- a/src/crt/i48rems.src +++ b/src/crt/i48rems.src @@ -29,12 +29,13 @@ __i48rems: lea de, iy push bc pop hl - ; negate remainder if dividend was negative - call m, __i48neg pop bc pop iy - ret + + ret p + ; negate remainder if dividend was negative + jp __i48neg extern __i48dvrmu extern __i48neg diff --git a/src/crt/lldivs.src b/src/crt/lldivs.src index 75c166e40..8896b4d87 100644 --- a/src/crt/lldivs.src +++ b/src/crt/lldivs.src @@ -65,11 +65,10 @@ __lldivs: bit 7, (iy + 23) .div_quotient_skip: - call nz, __llneg - pop iy - ret + ret z + jp __llneg extern __lldvrmu.hijack extern __llneg diff --git a/src/libc/i48div.src b/src/libc/i48div.src index a511137a3..b17d327f9 100644 --- a/src/libc/i48div.src +++ b/src/libc/i48div.src @@ -6,27 +6,37 @@ ; i48div_t i48div(int48_t numer, int48_t denom); _i48div: - ; TODO: __i48dvrmu is called twice here push ix ld ix, 0 add ix, sp + ld c, (ix + 14) + ld b, (ix + 20) + ld a, c + xor a, b + ld a, c + push af + rla ld hl, (ix + 9) ld de, (ix + 12) + call c, __i48neg + ld a, b + rla ld bc, (ix + 15) ld iy, (ix + 18) + call c, __uiyubcNeg + call __i48dvrmu ld ix, (ix + 6) - push hl - push de - call __i48divs + pop af + call m, __i48neg ld (ix + 0), hl ld (ix + 3), de - pop de - pop hl - call __i48rems - ld (ix + 6), hl - ld (ix + 9), de + rla + call c, __uiyubcNeg + ld (ix + 6), bc + ld (ix + 9), iy pop ix ret - extern __i48divs - extern __i48rems + extern __i48dvrmu + extern __i48neg + extern __uiyubcNeg