From 2d2f34f3dcf9bd4992a068f11ff183d891012d9f Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 16 Nov 2024 12:15:55 +0100 Subject: [PATCH] refact: move runtime arith functions to its own folder --- src/arch/z80/backend/runtime/core.py | 48 +- .../arch/zx48k/runtime/{ => arith}/_mul32.asm | 1 - .../runtime => zx48k/runtime/arith}/addf.asm | 1 - .../arch/zx48k/runtime/{ => arith}/div16.asm | 1 - .../runtime => zx48k/runtime/arith}/div32.asm | 1 - .../arch/zx48k/runtime/{ => arith}/div8.asm | 1 - .../runtime => zx48k/runtime/arith}/divf.asm | 1 - .../arch/zx48k/runtime/{ => arith}/divf16.asm | 3 +- .../arch/zx48k/runtime/{ => arith}/modf.asm | 0 .../runtime/arith}/modf16.asm | 5 +- .../arch/zx48k/runtime/{ => arith}/mul16.asm | 1 - .../arch/zx48k/runtime/{ => arith}/mul32.asm | 3 +- .../arch/zx48k/runtime/{ => arith}/mul8.asm | 1 - .../arch/zx48k/runtime/{ => arith}/mulf.asm | 1 - .../arch/zx48k/runtime/{ => arith}/mulf16.asm | 3 +- .../arch/zx48k/runtime/{ => arith}/sub32.asm | 0 .../runtime => zx48k/runtime/arith}/subf.asm | 1 - src/lib/arch/zx48k/runtime/array.asm | 2 +- src/lib/arch/zx48k/runtime/lei32.asm | 2 +- src/lib/arch/zx48k/runtime/lti32.asm | 2 +- src/lib/arch/zx48k/runtime/printi16.asm | 3 +- src/lib/arch/zx48k/runtime/printi32.asm | 3 +- src/lib/arch/zx48k/runtime/printi8.asm | 3 +- .../zxnext/runtime/{ => arith}/_mul32.asm | 1 - .../runtime => zxnext/runtime/arith}/addf.asm | 1 - .../arch/zxnext/runtime/{ => arith}/div16.asm | 1 - .../runtime/arith}/div32.asm | 1 - .../arch/zxnext/runtime/{ => arith}/div8.asm | 1 - .../runtime => zxnext/runtime/arith}/divf.asm | 1 - .../zxnext/runtime/{ => arith}/divf16.asm | 3 +- .../arch/zxnext/runtime/{ => arith}/modf.asm | 0 .../runtime/arith}/modf16.asm | 5 +- .../arch/zxnext/runtime/{ => arith}/mul16.asm | 0 .../arch/zxnext/runtime/{ => arith}/mul32.asm | 4 +- .../arch/zxnext/runtime/{ => arith}/mul8.asm | 10 +- .../arch/zxnext/runtime/{ => arith}/mulf.asm | 1 - .../zxnext/runtime/{ => arith}/mulf16.asm | 3 +- .../arch/zxnext/runtime/{ => arith}/sub32.asm | 0 .../runtime => zxnext/runtime/arith}/subf.asm | 1 - src/lib/arch/zxnext/runtime/array.asm | 2 +- src/lib/arch/zxnext/runtime/lei32.asm | 2 +- src/lib/arch/zxnext/runtime/lti32.asm | 2 +- src/lib/arch/zxnext/runtime/printi16.asm | 3 +- src/lib/arch/zxnext/runtime/printi32.asm | 3 +- src/lib/arch/zxnext/runtime/printi8.asm | 3 +- tests/functional/arch/zx48k/coercion1.asm | 176 +-- tests/functional/arch/zx48k/fact.asm | 233 +-- tests/functional/arch/zx48k/fporder.asm | 76 +- tests/functional/arch/zx48k/ifcrash.asm | 148 +- tests/functional/arch/zx48k/loadu16ii.asm | 58 +- tests/functional/arch/zx48k/ltee9.asm | 79 +- tests/functional/arch/zx48k/mcleod.asm | 130 +- tests/functional/arch/zx48k/opt3_data2.asm | 100 +- tests/functional/arch/zx48k/pooky0.asm | 68 +- tests/functional/arch/zx48k/read10.asm | 122 +- tests/functional/arch/zx48k/read13.asm | 378 ++--- tests/functional/arch/zx48k/read4.asm | 122 +- tests/functional/arch/zx48k/read5.asm | 122 +- tests/functional/arch/zx48k/read8.asm | 122 +- tests/functional/arch/zx48k/read9.asm | 128 +- tests/functional/arch/zx48k/subf00.asm | 122 +- tests/functional/arch/zx48k/subf01.asm | 66 +- tests/functional/arch/zx48k/subrec.asm | 233 +-- tests/functional/arch/zxnext/add16.asm | 49 + tests/functional/arch/zxnext/add16.bas | 10 + tests/functional/arch/zxnext/add16a.asm | 40 + tests/functional/arch/zxnext/add16a.bas | 4 + tests/functional/arch/zxnext/add16b.asm | 47 + tests/functional/arch/zxnext/add16b.bas | 4 + tests/functional/arch/zxnext/add32.asm | 73 + tests/functional/arch/zxnext/add32.bas | 10 + tests/functional/arch/zxnext/add32a.asm | 49 + tests/functional/arch/zxnext/add32a.bas | 4 + tests/functional/arch/zxnext/add32b.asm | 64 + tests/functional/arch/zxnext/add32b.bas | 3 + tests/functional/arch/zxnext/add8.asm | 49 + tests/functional/arch/zxnext/add8.bas | 10 + tests/functional/arch/zxnext/add8a.asm | 40 + tests/functional/arch/zxnext/add8a.bas | 4 + tests/functional/arch/zxnext/add8b.asm | 46 + tests/functional/arch/zxnext/add8b.bas | 4 + tests/functional/arch/zxnext/addf.asm | 174 +++ tests/functional/arch/zxnext/addf.bas | 3 + tests/functional/arch/zxnext/addf16.asm | 73 + tests/functional/arch/zxnext/addf16.bas | 10 + tests/functional/arch/zxnext/addf16a.asm | 49 + tests/functional/arch/zxnext/addf16a.bas | 4 + tests/functional/arch/zxnext/addf16b.asm | 64 + tests/functional/arch/zxnext/addf16b.bas | 4 + tests/functional/arch/zxnext/addstr.asm | 682 +++++++++ tests/functional/arch/zxnext/addstr.bas | 10 + tests/functional/arch/zxnext/array00.asm | 72 + tests/functional/arch/zxnext/array00.bas | 6 + tests/functional/arch/zxnext/array01.asm | 54 + tests/functional/arch/zxnext/array01.bas | 5 + tests/functional/arch/zxnext/array02.asm | 56 + tests/functional/arch/zxnext/array02.bas | 6 + tests/functional/arch/zxnext/array03.asm | 221 +++ tests/functional/arch/zxnext/array03.bas | 6 + tests/functional/arch/zxnext/array04.asm | 65 + tests/functional/arch/zxnext/array04.bas | 5 + tests/functional/arch/zxnext/array05.asm | 67 + tests/functional/arch/zxnext/array05.bas | 6 + tests/functional/arch/zxnext/array06.asm | 232 +++ tests/functional/arch/zxnext/array06.bas | 6 + tests/functional/arch/zxnext/array07.asm | 966 ++++++++++++ tests/functional/arch/zxnext/array07.bas | 6 + tests/functional/arch/zxnext/array08.asm | 946 ++++++++++++ tests/functional/arch/zxnext/array08.bas | 5 + tests/functional/arch/zxnext/array09.asm | 946 ++++++++++++ tests/functional/arch/zxnext/array09.bas | 6 + tests/functional/arch/zxnext/div32.asm | 272 ++++ tests/functional/arch/zxnext/div32.bas | 3 + tests/functional/arch/zxnext/divf00.asm | 231 +++ tests/functional/arch/zxnext/divf00.bas | 4 + tests/functional/arch/zxnext/divf01.asm | 207 +++ tests/functional/arch/zxnext/divf01.bas | 4 + tests/functional/arch/zxnext/divf16.asm | 313 ++++ tests/functional/arch/zxnext/divf16.bas | 13 + tests/functional/arch/zxnext/divf16a.asm | 258 ++++ tests/functional/arch/zxnext/divf16a.bas | 4 + tests/functional/arch/zxnext/divf16b.asm | 268 ++++ tests/functional/arch/zxnext/divf16b.bas | 4 + tests/functional/arch/zxnext/divf16c.asm | 318 ++++ tests/functional/arch/zxnext/divf16c.bas | 9 + tests/functional/arch/zxnext/divi16a.asm | 132 ++ tests/functional/arch/zxnext/divi16a.bas | 4 + tests/functional/arch/zxnext/divi16b.asm | 139 ++ tests/functional/arch/zxnext/divi16b.bas | 4 + tests/functional/arch/zxnext/divi32c.asm | 250 +++ tests/functional/arch/zxnext/divi32c.bas | 9 + tests/functional/arch/zxnext/divi8.asm | 128 ++ tests/functional/arch/zxnext/divi8.bas | 13 + tests/functional/arch/zxnext/divi8a.asm | 105 ++ tests/functional/arch/zxnext/divi8a.bas | 4 + tests/functional/arch/zxnext/divi8b.asm | 111 ++ tests/functional/arch/zxnext/divi8b.bas | 4 + tests/functional/arch/zxnext/divu16.asm | 156 ++ tests/functional/arch/zxnext/divu16.bas | 13 + tests/functional/arch/zxnext/divu16a.asm | 132 ++ tests/functional/arch/zxnext/divu16a.bas | 4 + tests/functional/arch/zxnext/divu16b.asm | 139 ++ tests/functional/arch/zxnext/divu16b.bas | 4 + tests/functional/arch/zxnext/divu32c.asm | 250 +++ tests/functional/arch/zxnext/divu32c.bas | 9 + tests/functional/arch/zxnext/divu8.asm | 128 ++ tests/functional/arch/zxnext/divu8.bas | 13 + tests/functional/arch/zxnext/divu8a.asm | 105 ++ tests/functional/arch/zxnext/divu8a.bas | 4 + tests/functional/arch/zxnext/divu8b.asm | 111 ++ tests/functional/arch/zxnext/divu8b.bas | 4 + tests/functional/arch/zxnext/lei32.asm | 185 +++ tests/functional/arch/zxnext/lei32.bas | 9 + tests/functional/arch/zxnext/lei8.asm | 84 ++ tests/functional/arch/zxnext/lei8.bas | 9 + tests/functional/arch/zxnext/lti32c.asm | 177 +++ tests/functional/arch/zxnext/lti32c.bas | 9 + tests/functional/arch/zxnext/modf.asm | 159 ++ tests/functional/arch/zxnext/modf.bas | 3 + tests/functional/arch/zxnext/modf16.asm | 40 + tests/functional/arch/zxnext/modf16.bas | 3 + tests/functional/arch/zxnext/modf16c.asm | 443 ++++++ tests/functional/arch/zxnext/modf16c.bas | 9 + tests/functional/arch/zxnext/modi32c.asm | 250 +++ tests/functional/arch/zxnext/modi32c.bas | 9 + tests/functional/arch/zxnext/modi8.asm | 127 ++ tests/functional/arch/zxnext/modi8.bas | 13 + tests/functional/arch/zxnext/modi8a.asm | 105 ++ tests/functional/arch/zxnext/modi8a.bas | 4 + tests/functional/arch/zxnext/modi8b.asm | 111 ++ tests/functional/arch/zxnext/modi8b.bas | 4 + tests/functional/arch/zxnext/modu32c.asm | 250 +++ tests/functional/arch/zxnext/modu32c.bas | 9 + tests/functional/arch/zxnext/modu8.asm | 127 ++ tests/functional/arch/zxnext/modu8.bas | 13 + tests/functional/arch/zxnext/modu8a.asm | 105 ++ tests/functional/arch/zxnext/modu8a.bas | 4 + tests/functional/arch/zxnext/modu8b.asm | 111 ++ tests/functional/arch/zxnext/modu8b.bas | 4 + tests/functional/arch/zxnext/mul16.asm | 76 + tests/functional/arch/zxnext/mul16.bas | 10 + tests/functional/arch/zxnext/mul16a.asm | 69 + tests/functional/arch/zxnext/mul16a.bas | 4 + tests/functional/arch/zxnext/mul16b.asm | 75 + tests/functional/arch/zxnext/mul16b.bas | 4 + tests/functional/arch/zxnext/mul16c.asm | 66 + tests/functional/arch/zxnext/mul16c.bas | 3 + tests/functional/arch/zxnext/mul8.asm | 66 + tests/functional/arch/zxnext/mul8.bas | 14 + tests/functional/arch/zxnext/mul8a.asm | 45 + tests/functional/arch/zxnext/mul8a.bas | 4 + tests/functional/arch/zxnext/mul8b.asm | 53 + tests/functional/arch/zxnext/mul8b.bas | 4 + tests/functional/arch/zxnext/mulf00.asm | 167 ++ tests/functional/arch/zxnext/mulf00.bas | 4 + tests/functional/arch/zxnext/mulf01.asm | 143 ++ tests/functional/arch/zxnext/mulf01.bas | 4 + tests/functional/arch/zxnext/mulf16.asm | 188 +++ tests/functional/arch/zxnext/mulf16.bas | 10 + tests/functional/arch/zxnext/mulf16a.asm | 172 +++ tests/functional/arch/zxnext/mulf16a.bas | 4 + tests/functional/arch/zxnext/print_f.asm | 1340 +++++++++++++++++ tests/functional/arch/zxnext/print_f.bas | 2 + tests/functional/arch/zxnext/print_i16.asm | 1097 ++++++++++++++ tests/functional/arch/zxnext/print_i16.bas | 2 + tests/functional/arch/zxnext/print_i32.asm | 1151 ++++++++++++++ tests/functional/arch/zxnext/print_i32.bas | 2 + tests/functional/arch/zxnext/print_i8.asm | 1071 +++++++++++++ tests/functional/arch/zxnext/print_i8.bas | 2 + tests/functional/arch/zxnext/print_u16.asm | 1099 ++++++++++++++ tests/functional/arch/zxnext/print_u16.bas | 2 + tests/functional/arch/zxnext/print_u32.asm | 1153 ++++++++++++++ tests/functional/arch/zxnext/print_u32.bas | 2 + tests/functional/arch/zxnext/print_u8.asm | 1073 +++++++++++++ tests/functional/arch/zxnext/print_u8.bas | 2 + tests/functional/arch/zxnext/sub16.asm | 55 + tests/functional/arch/zxnext/sub16.bas | 10 + tests/functional/arch/zxnext/sub16a.asm | 41 + tests/functional/arch/zxnext/sub16a.bas | 4 + tests/functional/arch/zxnext/sub16b.asm | 49 + tests/functional/arch/zxnext/sub16b.bas | 4 + tests/functional/arch/zxnext/sub8.asm | 51 + tests/functional/arch/zxnext/sub8.bas | 10 + tests/functional/arch/zxnext/sub8a.asm | 39 + tests/functional/arch/zxnext/sub8a.bas | 4 + tests/functional/arch/zxnext/sub8b.asm | 45 + tests/functional/arch/zxnext/sub8b.bas | 4 + tests/functional/arch/zxnext/subf00.asm | 168 +++ tests/functional/arch/zxnext/subf00.bas | 4 + tests/functional/arch/zxnext/subf01.asm | 144 ++ tests/functional/arch/zxnext/subf01.bas | 4 + tests/functional/arch/zxnext/subf16c.asm | 116 ++ tests/functional/arch/zxnext/subf16c.bas | 7 + tests/functional/arch/zxnext/subi32c.asm | 125 ++ tests/functional/arch/zxnext/subi32c.bas | 8 + 235 files changed, 22894 insertions(+), 1323 deletions(-) rename src/lib/arch/zx48k/runtime/{ => arith}/_mul32.asm (99%) rename src/lib/arch/{zxnext/runtime => zx48k/runtime/arith}/addf.asm (99%) rename src/lib/arch/zx48k/runtime/{ => arith}/div16.asm (99%) rename src/lib/arch/{zxnext/runtime => zx48k/runtime/arith}/div32.asm (99%) rename src/lib/arch/zx48k/runtime/{ => arith}/div8.asm (99%) rename src/lib/arch/{zxnext/runtime => zx48k/runtime/arith}/divf.asm (99%) rename src/lib/arch/zx48k/runtime/{ => arith}/divf16.asm (98%) rename src/lib/arch/zx48k/runtime/{ => arith}/modf.asm (100%) rename src/lib/arch/{zxnext/runtime => zx48k/runtime/arith}/modf16.asm (92%) rename src/lib/arch/zx48k/runtime/{ => arith}/mul16.asm (99%) rename src/lib/arch/zx48k/runtime/{ => arith}/mul32.asm (93%) rename src/lib/arch/zx48k/runtime/{ => arith}/mul8.asm (99%) rename src/lib/arch/zx48k/runtime/{ => arith}/mulf.asm (99%) rename src/lib/arch/zx48k/runtime/{ => arith}/mulf16.asm (97%) rename src/lib/arch/zx48k/runtime/{ => arith}/sub32.asm (100%) rename src/lib/arch/{zxnext/runtime => zx48k/runtime/arith}/subf.asm (99%) rename src/lib/arch/zxnext/runtime/{ => arith}/_mul32.asm (99%) rename src/lib/arch/{zx48k/runtime => zxnext/runtime/arith}/addf.asm (99%) rename src/lib/arch/zxnext/runtime/{ => arith}/div16.asm (99%) rename src/lib/arch/{zx48k/runtime => zxnext/runtime/arith}/div32.asm (99%) rename src/lib/arch/zxnext/runtime/{ => arith}/div8.asm (99%) rename src/lib/arch/{zx48k/runtime => zxnext/runtime/arith}/divf.asm (99%) rename src/lib/arch/zxnext/runtime/{ => arith}/divf16.asm (98%) rename src/lib/arch/zxnext/runtime/{ => arith}/modf.asm (100%) rename src/lib/arch/{zx48k/runtime => zxnext/runtime/arith}/modf16.asm (92%) rename src/lib/arch/zxnext/runtime/{ => arith}/mul16.asm (100%) rename src/lib/arch/zxnext/runtime/{ => arith}/mul32.asm (93%) rename src/lib/arch/zxnext/runtime/{ => arith}/mul8.asm (83%) rename src/lib/arch/zxnext/runtime/{ => arith}/mulf.asm (99%) rename src/lib/arch/zxnext/runtime/{ => arith}/mulf16.asm (97%) rename src/lib/arch/zxnext/runtime/{ => arith}/sub32.asm (100%) rename src/lib/arch/{zx48k/runtime => zxnext/runtime/arith}/subf.asm (99%) create mode 100644 tests/functional/arch/zxnext/add16.asm create mode 100644 tests/functional/arch/zxnext/add16.bas create mode 100644 tests/functional/arch/zxnext/add16a.asm create mode 100644 tests/functional/arch/zxnext/add16a.bas create mode 100644 tests/functional/arch/zxnext/add16b.asm create mode 100644 tests/functional/arch/zxnext/add16b.bas create mode 100644 tests/functional/arch/zxnext/add32.asm create mode 100644 tests/functional/arch/zxnext/add32.bas create mode 100644 tests/functional/arch/zxnext/add32a.asm create mode 100644 tests/functional/arch/zxnext/add32a.bas create mode 100644 tests/functional/arch/zxnext/add32b.asm create mode 100644 tests/functional/arch/zxnext/add32b.bas create mode 100644 tests/functional/arch/zxnext/add8.asm create mode 100644 tests/functional/arch/zxnext/add8.bas create mode 100644 tests/functional/arch/zxnext/add8a.asm create mode 100644 tests/functional/arch/zxnext/add8a.bas create mode 100644 tests/functional/arch/zxnext/add8b.asm create mode 100644 tests/functional/arch/zxnext/add8b.bas create mode 100644 tests/functional/arch/zxnext/addf.asm create mode 100644 tests/functional/arch/zxnext/addf.bas create mode 100644 tests/functional/arch/zxnext/addf16.asm create mode 100644 tests/functional/arch/zxnext/addf16.bas create mode 100644 tests/functional/arch/zxnext/addf16a.asm create mode 100644 tests/functional/arch/zxnext/addf16a.bas create mode 100644 tests/functional/arch/zxnext/addf16b.asm create mode 100644 tests/functional/arch/zxnext/addf16b.bas create mode 100644 tests/functional/arch/zxnext/addstr.asm create mode 100644 tests/functional/arch/zxnext/addstr.bas create mode 100644 tests/functional/arch/zxnext/array00.asm create mode 100644 tests/functional/arch/zxnext/array00.bas create mode 100644 tests/functional/arch/zxnext/array01.asm create mode 100644 tests/functional/arch/zxnext/array01.bas create mode 100644 tests/functional/arch/zxnext/array02.asm create mode 100644 tests/functional/arch/zxnext/array02.bas create mode 100644 tests/functional/arch/zxnext/array03.asm create mode 100644 tests/functional/arch/zxnext/array03.bas create mode 100644 tests/functional/arch/zxnext/array04.asm create mode 100644 tests/functional/arch/zxnext/array04.bas create mode 100644 tests/functional/arch/zxnext/array05.asm create mode 100644 tests/functional/arch/zxnext/array05.bas create mode 100644 tests/functional/arch/zxnext/array06.asm create mode 100644 tests/functional/arch/zxnext/array06.bas create mode 100644 tests/functional/arch/zxnext/array07.asm create mode 100644 tests/functional/arch/zxnext/array07.bas create mode 100644 tests/functional/arch/zxnext/array08.asm create mode 100644 tests/functional/arch/zxnext/array08.bas create mode 100644 tests/functional/arch/zxnext/array09.asm create mode 100644 tests/functional/arch/zxnext/array09.bas create mode 100644 tests/functional/arch/zxnext/div32.asm create mode 100644 tests/functional/arch/zxnext/div32.bas create mode 100644 tests/functional/arch/zxnext/divf00.asm create mode 100644 tests/functional/arch/zxnext/divf00.bas create mode 100644 tests/functional/arch/zxnext/divf01.asm create mode 100644 tests/functional/arch/zxnext/divf01.bas create mode 100644 tests/functional/arch/zxnext/divf16.asm create mode 100644 tests/functional/arch/zxnext/divf16.bas create mode 100644 tests/functional/arch/zxnext/divf16a.asm create mode 100644 tests/functional/arch/zxnext/divf16a.bas create mode 100644 tests/functional/arch/zxnext/divf16b.asm create mode 100644 tests/functional/arch/zxnext/divf16b.bas create mode 100644 tests/functional/arch/zxnext/divf16c.asm create mode 100644 tests/functional/arch/zxnext/divf16c.bas create mode 100644 tests/functional/arch/zxnext/divi16a.asm create mode 100644 tests/functional/arch/zxnext/divi16a.bas create mode 100644 tests/functional/arch/zxnext/divi16b.asm create mode 100644 tests/functional/arch/zxnext/divi16b.bas create mode 100644 tests/functional/arch/zxnext/divi32c.asm create mode 100644 tests/functional/arch/zxnext/divi32c.bas create mode 100644 tests/functional/arch/zxnext/divi8.asm create mode 100644 tests/functional/arch/zxnext/divi8.bas create mode 100644 tests/functional/arch/zxnext/divi8a.asm create mode 100644 tests/functional/arch/zxnext/divi8a.bas create mode 100644 tests/functional/arch/zxnext/divi8b.asm create mode 100644 tests/functional/arch/zxnext/divi8b.bas create mode 100644 tests/functional/arch/zxnext/divu16.asm create mode 100644 tests/functional/arch/zxnext/divu16.bas create mode 100644 tests/functional/arch/zxnext/divu16a.asm create mode 100644 tests/functional/arch/zxnext/divu16a.bas create mode 100644 tests/functional/arch/zxnext/divu16b.asm create mode 100644 tests/functional/arch/zxnext/divu16b.bas create mode 100644 tests/functional/arch/zxnext/divu32c.asm create mode 100644 tests/functional/arch/zxnext/divu32c.bas create mode 100644 tests/functional/arch/zxnext/divu8.asm create mode 100644 tests/functional/arch/zxnext/divu8.bas create mode 100644 tests/functional/arch/zxnext/divu8a.asm create mode 100644 tests/functional/arch/zxnext/divu8a.bas create mode 100644 tests/functional/arch/zxnext/divu8b.asm create mode 100644 tests/functional/arch/zxnext/divu8b.bas create mode 100644 tests/functional/arch/zxnext/lei32.asm create mode 100644 tests/functional/arch/zxnext/lei32.bas create mode 100644 tests/functional/arch/zxnext/lei8.asm create mode 100644 tests/functional/arch/zxnext/lei8.bas create mode 100644 tests/functional/arch/zxnext/lti32c.asm create mode 100644 tests/functional/arch/zxnext/lti32c.bas create mode 100644 tests/functional/arch/zxnext/modf.asm create mode 100644 tests/functional/arch/zxnext/modf.bas create mode 100644 tests/functional/arch/zxnext/modf16.asm create mode 100644 tests/functional/arch/zxnext/modf16.bas create mode 100644 tests/functional/arch/zxnext/modf16c.asm create mode 100644 tests/functional/arch/zxnext/modf16c.bas create mode 100644 tests/functional/arch/zxnext/modi32c.asm create mode 100644 tests/functional/arch/zxnext/modi32c.bas create mode 100644 tests/functional/arch/zxnext/modi8.asm create mode 100644 tests/functional/arch/zxnext/modi8.bas create mode 100644 tests/functional/arch/zxnext/modi8a.asm create mode 100644 tests/functional/arch/zxnext/modi8a.bas create mode 100644 tests/functional/arch/zxnext/modi8b.asm create mode 100644 tests/functional/arch/zxnext/modi8b.bas create mode 100644 tests/functional/arch/zxnext/modu32c.asm create mode 100644 tests/functional/arch/zxnext/modu32c.bas create mode 100644 tests/functional/arch/zxnext/modu8.asm create mode 100644 tests/functional/arch/zxnext/modu8.bas create mode 100644 tests/functional/arch/zxnext/modu8a.asm create mode 100644 tests/functional/arch/zxnext/modu8a.bas create mode 100644 tests/functional/arch/zxnext/modu8b.asm create mode 100644 tests/functional/arch/zxnext/modu8b.bas create mode 100644 tests/functional/arch/zxnext/mul16.asm create mode 100644 tests/functional/arch/zxnext/mul16.bas create mode 100644 tests/functional/arch/zxnext/mul16a.asm create mode 100644 tests/functional/arch/zxnext/mul16a.bas create mode 100644 tests/functional/arch/zxnext/mul16b.asm create mode 100644 tests/functional/arch/zxnext/mul16b.bas create mode 100644 tests/functional/arch/zxnext/mul16c.asm create mode 100644 tests/functional/arch/zxnext/mul16c.bas create mode 100644 tests/functional/arch/zxnext/mul8.asm create mode 100644 tests/functional/arch/zxnext/mul8.bas create mode 100644 tests/functional/arch/zxnext/mul8a.asm create mode 100644 tests/functional/arch/zxnext/mul8a.bas create mode 100644 tests/functional/arch/zxnext/mul8b.asm create mode 100644 tests/functional/arch/zxnext/mul8b.bas create mode 100644 tests/functional/arch/zxnext/mulf00.asm create mode 100644 tests/functional/arch/zxnext/mulf00.bas create mode 100644 tests/functional/arch/zxnext/mulf01.asm create mode 100644 tests/functional/arch/zxnext/mulf01.bas create mode 100644 tests/functional/arch/zxnext/mulf16.asm create mode 100644 tests/functional/arch/zxnext/mulf16.bas create mode 100644 tests/functional/arch/zxnext/mulf16a.asm create mode 100644 tests/functional/arch/zxnext/mulf16a.bas create mode 100644 tests/functional/arch/zxnext/print_f.asm create mode 100644 tests/functional/arch/zxnext/print_f.bas create mode 100644 tests/functional/arch/zxnext/print_i16.asm create mode 100644 tests/functional/arch/zxnext/print_i16.bas create mode 100644 tests/functional/arch/zxnext/print_i32.asm create mode 100644 tests/functional/arch/zxnext/print_i32.bas create mode 100644 tests/functional/arch/zxnext/print_i8.asm create mode 100644 tests/functional/arch/zxnext/print_i8.bas create mode 100644 tests/functional/arch/zxnext/print_u16.asm create mode 100644 tests/functional/arch/zxnext/print_u16.bas create mode 100644 tests/functional/arch/zxnext/print_u32.asm create mode 100644 tests/functional/arch/zxnext/print_u32.bas create mode 100644 tests/functional/arch/zxnext/print_u8.asm create mode 100644 tests/functional/arch/zxnext/print_u8.bas create mode 100644 tests/functional/arch/zxnext/sub16.asm create mode 100644 tests/functional/arch/zxnext/sub16.bas create mode 100644 tests/functional/arch/zxnext/sub16a.asm create mode 100644 tests/functional/arch/zxnext/sub16a.bas create mode 100644 tests/functional/arch/zxnext/sub16b.asm create mode 100644 tests/functional/arch/zxnext/sub16b.bas create mode 100644 tests/functional/arch/zxnext/sub8.asm create mode 100644 tests/functional/arch/zxnext/sub8.bas create mode 100644 tests/functional/arch/zxnext/sub8a.asm create mode 100644 tests/functional/arch/zxnext/sub8a.bas create mode 100644 tests/functional/arch/zxnext/sub8b.asm create mode 100644 tests/functional/arch/zxnext/sub8b.bas create mode 100644 tests/functional/arch/zxnext/subf00.asm create mode 100644 tests/functional/arch/zxnext/subf00.bas create mode 100644 tests/functional/arch/zxnext/subf01.asm create mode 100644 tests/functional/arch/zxnext/subf01.bas create mode 100644 tests/functional/arch/zxnext/subf16c.asm create mode 100644 tests/functional/arch/zxnext/subf16c.bas create mode 100644 tests/functional/arch/zxnext/subi32c.asm create mode 100644 tests/functional/arch/zxnext/subi32c.bas diff --git a/src/arch/z80/backend/runtime/core.py b/src/arch/z80/backend/runtime/core.py index 58bdb1272..dc5f3977d 100644 --- a/src/arch/z80/backend/runtime/core.py +++ b/src/arch/z80/backend/runtime/core.py @@ -136,7 +136,7 @@ class CoreLabels: CoreLabels.ABS16: "abs16.asm", CoreLabels.ABS8: "abs8.asm", CoreLabels.ABS32: "abs32.asm", - CoreLabels.ADDF: "addf.asm", + CoreLabels.ADDF: "arith/addf.asm", CoreLabels.ADDSTR: "strcat.asm", CoreLabels.ALLOC_INITIALIZED_LOCAL_ARRAY: "arrayalloc.asm", CoreLabels.ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: "arrayalloc.asm", @@ -157,14 +157,14 @@ class CoreLabels: CoreLabels.BXOR16: "bitwise/bxor16.asm", CoreLabels.BXOR32: "bitwise/bxor32.asm", CoreLabels.CHECK_BREAK: "break.asm", - CoreLabels.DIVF: "divf.asm", - CoreLabels.DIVF16: "divf16.asm", - CoreLabels.DIVI16: "div16.asm", - CoreLabels.DIVI32: "div32.asm", - CoreLabels.DIVU16: "div16.asm", - CoreLabels.DIVU32: "div32.asm", - CoreLabels.DIVI8_FAST: "div8.asm", - CoreLabels.DIVU8_FAST: "div8.asm", + CoreLabels.DIVF: "arith/divf.asm", + CoreLabels.DIVF16: "arith/divf16.asm", + CoreLabels.DIVI16: "arith/div16.asm", + CoreLabels.DIVI32: "arith/div32.asm", + CoreLabels.DIVU16: "arith/div16.asm", + CoreLabels.DIVU32: "arith/div32.asm", + CoreLabels.DIVI8_FAST: "arith/div8.asm", + CoreLabels.DIVU8_FAST: "arith/div8.asm", CoreLabels.GEF: "gef.asm", CoreLabels.GTF: "gtf.asm", CoreLabels.I8TOFREG: "u32tofreg.asm", @@ -196,19 +196,19 @@ class CoreLabels: CoreLabels.LTI8: "lti8.asm", CoreLabels.LTI32: "lti32.asm", CoreLabels.MEM_FREE: "free.asm", - CoreLabels.MODF: "modf.asm", - CoreLabels.MODF16: "modf16.asm", - CoreLabels.MODI16: "div16.asm", - CoreLabels.MODI32: "div32.asm", - CoreLabels.MODU16: "div16.asm", - CoreLabels.MODU32: "div32.asm", - CoreLabels.MODI8_FAST: "div8.asm", - CoreLabels.MODU8_FAST: "div8.asm", - CoreLabels.MUL8_FAST: "mul8.asm", - CoreLabels.MUL16_FAST: "mul16.asm", - CoreLabels.MUL32: "mul32.asm", - CoreLabels.MULF: "mulf.asm", - CoreLabels.MULF16: "mulf16.asm", + CoreLabels.MODF: "arith/modf.asm", + CoreLabels.MODF16: "arith/modf16.asm", + CoreLabels.MODI16: "arith/div16.asm", + CoreLabels.MODI32: "arith/div32.asm", + CoreLabels.MODU16: "arith/div16.asm", + CoreLabels.MODU32: "arith/div32.asm", + CoreLabels.MODI8_FAST: "arith/div8.asm", + CoreLabels.MODU8_FAST: "arith/div8.asm", + CoreLabels.MUL8_FAST: "arith/mul8.asm", + CoreLabels.MUL16_FAST: "arith/mul16.asm", + CoreLabels.MUL32: "arith/mul32.asm", + CoreLabels.MULF: "arith/mulf.asm", + CoreLabels.MULF16: "arith/mulf16.asm", CoreLabels.NEF: "nef.asm", CoreLabels.NEG32: "neg32.asm", CoreLabels.NEGF: "negf.asm", @@ -249,8 +249,8 @@ class CoreLabels: CoreLabels.STRLT: "string.asm", CoreLabels.STRNE: "string.asm", CoreLabels.STRSLICE: "strslice.asm", - CoreLabels.SUB32: "sub32.asm", - CoreLabels.SUBF: "subf.asm", + CoreLabels.SUB32: "arith/sub32.asm", + CoreLabels.SUBF: "arith/subf.asm", CoreLabels.SWAP32: "swap32.asm", CoreLabels.U32TOFREG: "u32tofreg.asm", CoreLabels.U8TOFREG: "u32tofreg.asm", diff --git a/src/lib/arch/zx48k/runtime/_mul32.asm b/src/lib/arch/zx48k/runtime/arith/_mul32.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/_mul32.asm rename to src/lib/arch/zx48k/runtime/arith/_mul32.asm index 2f7348103..60aa191a5 100644 --- a/src/lib/arch/zx48k/runtime/_mul32.asm +++ b/src/lib/arch/zx48k/runtime/arith/_mul32.asm @@ -68,4 +68,3 @@ __LMULSTART: ret ; result in h'l'hlb'c'ac pop namespace - diff --git a/src/lib/arch/zxnext/runtime/addf.asm b/src/lib/arch/zx48k/runtime/arith/addf.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/addf.asm rename to src/lib/arch/zx48k/runtime/arith/addf.asm index 50b02ab97..cc6b03c04 100644 --- a/src/lib/arch/zxnext/runtime/addf.asm +++ b/src/lib/arch/zx48k/runtime/arith/addf.asm @@ -22,4 +22,3 @@ __ADDF: ; Addition jp __FPSTACK_POP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/div16.asm b/src/lib/arch/zx48k/runtime/arith/div16.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/div16.asm rename to src/lib/arch/zx48k/runtime/arith/div16.asm index 437b89a5f..1eed19848 100644 --- a/src/lib/arch/zx48k/runtime/div16.asm +++ b/src/lib/arch/zx48k/runtime/arith/div16.asm @@ -102,4 +102,3 @@ __MODI16: ; 16 bit modulus ret pop namespace - diff --git a/src/lib/arch/zxnext/runtime/div32.asm b/src/lib/arch/zx48k/runtime/arith/div32.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/div32.asm rename to src/lib/arch/zx48k/runtime/arith/div32.asm index e3b9f5646..968a6879a 100644 --- a/src/lib/arch/zxnext/runtime/div32.asm +++ b/src/lib/arch/zx48k/runtime/arith/div32.asm @@ -142,4 +142,3 @@ __MODI32: ; 32bits signed division modulus jp __MODU32START pop namespace - diff --git a/src/lib/arch/zx48k/runtime/div8.asm b/src/lib/arch/zx48k/runtime/arith/div8.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/div8.asm rename to src/lib/arch/zx48k/runtime/arith/div8.asm index 0c66b3f5f..465a7c2d3 100644 --- a/src/lib/arch/zx48k/runtime/div8.asm +++ b/src/lib/arch/zx48k/runtime/arith/div8.asm @@ -88,4 +88,3 @@ __MODI8_FAST: ; __FASTCALL__ entry ret ; a = Modulus pop namespace - diff --git a/src/lib/arch/zxnext/runtime/divf.asm b/src/lib/arch/zx48k/runtime/arith/divf.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/divf.asm rename to src/lib/arch/zx48k/runtime/arith/divf.asm index fa5500a5c..d3b163fe4 100644 --- a/src/lib/arch/zxnext/runtime/divf.asm +++ b/src/lib/arch/zx48k/runtime/arith/divf.asm @@ -61,4 +61,3 @@ __DIVBYZERO: ENDP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/divf16.asm b/src/lib/arch/zx48k/runtime/arith/divf16.asm similarity index 98% rename from src/lib/arch/zx48k/runtime/divf16.asm rename to src/lib/arch/zx48k/runtime/arith/divf16.asm index 96cc03530..2961c62fb 100644 --- a/src/lib/arch/zx48k/runtime/divf16.asm +++ b/src/lib/arch/zx48k/runtime/arith/divf16.asm @@ -1,4 +1,4 @@ -#include once +#include once push namespace core @@ -84,4 +84,3 @@ __ENDF16DIV: ; Put the sign on the result jp __NEG32 ; Negates DEHL and returns from there pop namespace - diff --git a/src/lib/arch/zx48k/runtime/modf.asm b/src/lib/arch/zx48k/runtime/arith/modf.asm similarity index 100% rename from src/lib/arch/zx48k/runtime/modf.asm rename to src/lib/arch/zx48k/runtime/arith/modf.asm diff --git a/src/lib/arch/zxnext/runtime/modf16.asm b/src/lib/arch/zx48k/runtime/arith/modf16.asm similarity index 92% rename from src/lib/arch/zxnext/runtime/modf16.asm rename to src/lib/arch/zx48k/runtime/arith/modf16.asm index 774444bd7..edba61785 100644 --- a/src/lib/arch/zxnext/runtime/modf16.asm +++ b/src/lib/arch/zx48k/runtime/arith/modf16.asm @@ -1,7 +1,7 @@ ; Computes A % B for fixed values -#include once -#include once +#include once +#include once push namespace core @@ -38,4 +38,3 @@ TEMP EQU 23698 ; MEMBOT ENDP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/mul16.asm b/src/lib/arch/zx48k/runtime/arith/mul16.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/mul16.asm rename to src/lib/arch/zx48k/runtime/arith/mul16.asm index 2755ec238..1540d1a65 100644 --- a/src/lib/arch/zx48k/runtime/mul16.asm +++ b/src/lib/arch/zx48k/runtime/arith/mul16.asm @@ -33,4 +33,3 @@ __MUL16NOADD: ENDP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/mul32.asm b/src/lib/arch/zx48k/runtime/arith/mul32.asm similarity index 93% rename from src/lib/arch/zx48k/runtime/mul32.asm rename to src/lib/arch/zx48k/runtime/arith/mul32.asm index 9f259f508..fc6430221 100644 --- a/src/lib/arch/zx48k/runtime/mul32.asm +++ b/src/lib/arch/zx48k/runtime/arith/mul32.asm @@ -1,4 +1,4 @@ -#include once <_mul32.asm> +#include once push namespace core @@ -26,3 +26,4 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) pop namespace +f diff --git a/src/lib/arch/zx48k/runtime/mul8.asm b/src/lib/arch/zx48k/runtime/arith/mul8.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/mul8.asm rename to src/lib/arch/zx48k/runtime/arith/mul8.asm index 5b168a0cd..27b8e6785 100644 --- a/src/lib/arch/zx48k/runtime/mul8.asm +++ b/src/lib/arch/zx48k/runtime/arith/mul8.asm @@ -52,4 +52,3 @@ __MUL8B: ENDP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/mulf.asm b/src/lib/arch/zx48k/runtime/arith/mulf.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/mulf.asm rename to src/lib/arch/zx48k/runtime/arith/mulf.asm index 4999387f3..ea43970bf 100644 --- a/src/lib/arch/zx48k/runtime/mulf.asm +++ b/src/lib/arch/zx48k/runtime/arith/mulf.asm @@ -22,4 +22,3 @@ __MULF: ; Multiplication jp __FPSTACK_POP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/mulf16.asm b/src/lib/arch/zx48k/runtime/arith/mulf16.asm similarity index 97% rename from src/lib/arch/zx48k/runtime/mulf16.asm rename to src/lib/arch/zx48k/runtime/arith/mulf16.asm index 42d4d83c7..9c960669c 100644 --- a/src/lib/arch/zx48k/runtime/mulf16.asm +++ b/src/lib/arch/zx48k/runtime/arith/mulf16.asm @@ -1,5 +1,5 @@ #include once -#include once <_mul32.asm> +#include once push namespace core @@ -44,4 +44,3 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 ret pop namespace - diff --git a/src/lib/arch/zx48k/runtime/sub32.asm b/src/lib/arch/zx48k/runtime/arith/sub32.asm similarity index 100% rename from src/lib/arch/zx48k/runtime/sub32.asm rename to src/lib/arch/zx48k/runtime/arith/sub32.asm diff --git a/src/lib/arch/zxnext/runtime/subf.asm b/src/lib/arch/zx48k/runtime/arith/subf.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/subf.asm rename to src/lib/arch/zx48k/runtime/arith/subf.asm index 1f1482f91..e59d87180 100644 --- a/src/lib/arch/zxnext/runtime/subf.asm +++ b/src/lib/arch/zx48k/runtime/arith/subf.asm @@ -25,4 +25,3 @@ __SUBF: ; Subtraction jp __FPSTACK_POP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/array.asm b/src/lib/arch/zx48k/runtime/array.asm index b801186cf..154b65c4d 100644 --- a/src/lib/arch/zx48k/runtime/array.asm +++ b/src/lib/arch/zx48k/runtime/array.asm @@ -16,7 +16,7 @@ ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... -#include once +#include once #ifdef __CHECK_ARRAY_BOUNDARY__ #include once diff --git a/src/lib/arch/zx48k/runtime/lei32.asm b/src/lib/arch/zx48k/runtime/lei32.asm index a53db51ba..c79c094b6 100644 --- a/src/lib/arch/zx48k/runtime/lei32.asm +++ b/src/lib/arch/zx48k/runtime/lei32.asm @@ -1,5 +1,5 @@ -#include once +#include once push namespace core diff --git a/src/lib/arch/zx48k/runtime/lti32.asm b/src/lib/arch/zx48k/runtime/lti32.asm index f49ce48b9..2f3e100a4 100644 --- a/src/lib/arch/zx48k/runtime/lti32.asm +++ b/src/lib/arch/zx48k/runtime/lti32.asm @@ -1,5 +1,5 @@ -#include once +#include once push namespace core diff --git a/src/lib/arch/zx48k/runtime/printi16.asm b/src/lib/arch/zx48k/runtime/printi16.asm index 6eadfb7e7..d44b22cbf 100644 --- a/src/lib/arch/zx48k/runtime/printi16.asm +++ b/src/lib/arch/zx48k/runtime/printi16.asm @@ -1,5 +1,5 @@ #include once -#include once +#include once #include once #include once @@ -40,4 +40,3 @@ __PRINTU_LOOP: ENDP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/printi32.asm b/src/lib/arch/zx48k/runtime/printi32.asm index e619fa492..eba8e215a 100644 --- a/src/lib/arch/zx48k/runtime/printi32.asm +++ b/src/lib/arch/zx48k/runtime/printi32.asm @@ -1,6 +1,6 @@ #include once #include once -#include once +#include once #include once @@ -48,4 +48,3 @@ __PRINTU_LOOP: ENDP pop namespace - diff --git a/src/lib/arch/zx48k/runtime/printi8.asm b/src/lib/arch/zx48k/runtime/printi8.asm index dcfaaeb9e..a12ecbfad 100644 --- a/src/lib/arch/zx48k/runtime/printi8.asm +++ b/src/lib/arch/zx48k/runtime/printi8.asm @@ -1,5 +1,5 @@ #include once -#include once +#include once push namespace core @@ -39,4 +39,3 @@ __PRINTU_LOOP: ENDP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/_mul32.asm b/src/lib/arch/zxnext/runtime/arith/_mul32.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/_mul32.asm rename to src/lib/arch/zxnext/runtime/arith/_mul32.asm index 2f7348103..60aa191a5 100644 --- a/src/lib/arch/zxnext/runtime/_mul32.asm +++ b/src/lib/arch/zxnext/runtime/arith/_mul32.asm @@ -68,4 +68,3 @@ __LMULSTART: ret ; result in h'l'hlb'c'ac pop namespace - diff --git a/src/lib/arch/zx48k/runtime/addf.asm b/src/lib/arch/zxnext/runtime/arith/addf.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/addf.asm rename to src/lib/arch/zxnext/runtime/arith/addf.asm index 50b02ab97..cc6b03c04 100644 --- a/src/lib/arch/zx48k/runtime/addf.asm +++ b/src/lib/arch/zxnext/runtime/arith/addf.asm @@ -22,4 +22,3 @@ __ADDF: ; Addition jp __FPSTACK_POP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/div16.asm b/src/lib/arch/zxnext/runtime/arith/div16.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/div16.asm rename to src/lib/arch/zxnext/runtime/arith/div16.asm index 437b89a5f..1eed19848 100644 --- a/src/lib/arch/zxnext/runtime/div16.asm +++ b/src/lib/arch/zxnext/runtime/arith/div16.asm @@ -102,4 +102,3 @@ __MODI16: ; 16 bit modulus ret pop namespace - diff --git a/src/lib/arch/zx48k/runtime/div32.asm b/src/lib/arch/zxnext/runtime/arith/div32.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/div32.asm rename to src/lib/arch/zxnext/runtime/arith/div32.asm index e3b9f5646..968a6879a 100644 --- a/src/lib/arch/zx48k/runtime/div32.asm +++ b/src/lib/arch/zxnext/runtime/arith/div32.asm @@ -142,4 +142,3 @@ __MODI32: ; 32bits signed division modulus jp __MODU32START pop namespace - diff --git a/src/lib/arch/zxnext/runtime/div8.asm b/src/lib/arch/zxnext/runtime/arith/div8.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/div8.asm rename to src/lib/arch/zxnext/runtime/arith/div8.asm index 0c66b3f5f..465a7c2d3 100644 --- a/src/lib/arch/zxnext/runtime/div8.asm +++ b/src/lib/arch/zxnext/runtime/arith/div8.asm @@ -88,4 +88,3 @@ __MODI8_FAST: ; __FASTCALL__ entry ret ; a = Modulus pop namespace - diff --git a/src/lib/arch/zx48k/runtime/divf.asm b/src/lib/arch/zxnext/runtime/arith/divf.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/divf.asm rename to src/lib/arch/zxnext/runtime/arith/divf.asm index fa5500a5c..d3b163fe4 100644 --- a/src/lib/arch/zx48k/runtime/divf.asm +++ b/src/lib/arch/zxnext/runtime/arith/divf.asm @@ -61,4 +61,3 @@ __DIVBYZERO: ENDP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/divf16.asm b/src/lib/arch/zxnext/runtime/arith/divf16.asm similarity index 98% rename from src/lib/arch/zxnext/runtime/divf16.asm rename to src/lib/arch/zxnext/runtime/arith/divf16.asm index 96cc03530..2961c62fb 100644 --- a/src/lib/arch/zxnext/runtime/divf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/divf16.asm @@ -1,4 +1,4 @@ -#include once +#include once push namespace core @@ -84,4 +84,3 @@ __ENDF16DIV: ; Put the sign on the result jp __NEG32 ; Negates DEHL and returns from there pop namespace - diff --git a/src/lib/arch/zxnext/runtime/modf.asm b/src/lib/arch/zxnext/runtime/arith/modf.asm similarity index 100% rename from src/lib/arch/zxnext/runtime/modf.asm rename to src/lib/arch/zxnext/runtime/arith/modf.asm diff --git a/src/lib/arch/zx48k/runtime/modf16.asm b/src/lib/arch/zxnext/runtime/arith/modf16.asm similarity index 92% rename from src/lib/arch/zx48k/runtime/modf16.asm rename to src/lib/arch/zxnext/runtime/arith/modf16.asm index 774444bd7..edba61785 100644 --- a/src/lib/arch/zx48k/runtime/modf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/modf16.asm @@ -1,7 +1,7 @@ ; Computes A % B for fixed values -#include once -#include once +#include once +#include once push namespace core @@ -38,4 +38,3 @@ TEMP EQU 23698 ; MEMBOT ENDP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/mul16.asm b/src/lib/arch/zxnext/runtime/arith/mul16.asm similarity index 100% rename from src/lib/arch/zxnext/runtime/mul16.asm rename to src/lib/arch/zxnext/runtime/arith/mul16.asm diff --git a/src/lib/arch/zxnext/runtime/mul32.asm b/src/lib/arch/zxnext/runtime/arith/mul32.asm similarity index 93% rename from src/lib/arch/zxnext/runtime/mul32.asm rename to src/lib/arch/zxnext/runtime/arith/mul32.asm index 9f259f508..641bc1efb 100644 --- a/src/lib/arch/zxnext/runtime/mul32.asm +++ b/src/lib/arch/zxnext/runtime/arith/mul32.asm @@ -1,4 +1,4 @@ -#include once <_mul32.asm> +#include once push namespace core @@ -24,5 +24,3 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ret pop namespace - - diff --git a/src/lib/arch/zxnext/runtime/mul8.asm b/src/lib/arch/zxnext/runtime/arith/mul8.asm similarity index 83% rename from src/lib/arch/zxnext/runtime/mul8.asm rename to src/lib/arch/zxnext/runtime/arith/mul8.asm index 3f5728acf..a6d1b3fac 100644 --- a/src/lib/arch/zxnext/runtime/mul8.asm +++ b/src/lib/arch/zxnext/runtime/arith/mul8.asm @@ -7,13 +7,13 @@ __MUL8: ; Performs 8bit x 8bit multiplication ex (sp), hl ; CALLE convention __MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - ; zx next - ld e,a ; 4t - ld d,h ; 4t - mul d,e ; 8t + ; zx next + ld e,a ; 4t + ld d,h ; 4t + mul d,e ; 8t ld a,e ; 4 ; 20t ret ; result = DE & A - + ENDP pop namespace diff --git a/src/lib/arch/zxnext/runtime/mulf.asm b/src/lib/arch/zxnext/runtime/arith/mulf.asm similarity index 99% rename from src/lib/arch/zxnext/runtime/mulf.asm rename to src/lib/arch/zxnext/runtime/arith/mulf.asm index 4999387f3..ea43970bf 100644 --- a/src/lib/arch/zxnext/runtime/mulf.asm +++ b/src/lib/arch/zxnext/runtime/arith/mulf.asm @@ -22,4 +22,3 @@ __MULF: ; Multiplication jp __FPSTACK_POP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/mulf16.asm b/src/lib/arch/zxnext/runtime/arith/mulf16.asm similarity index 97% rename from src/lib/arch/zxnext/runtime/mulf16.asm rename to src/lib/arch/zxnext/runtime/arith/mulf16.asm index 42d4d83c7..9c960669c 100644 --- a/src/lib/arch/zxnext/runtime/mulf16.asm +++ b/src/lib/arch/zxnext/runtime/arith/mulf16.asm @@ -1,5 +1,5 @@ #include once -#include once <_mul32.asm> +#include once push namespace core @@ -44,4 +44,3 @@ __ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 ret pop namespace - diff --git a/src/lib/arch/zxnext/runtime/sub32.asm b/src/lib/arch/zxnext/runtime/arith/sub32.asm similarity index 100% rename from src/lib/arch/zxnext/runtime/sub32.asm rename to src/lib/arch/zxnext/runtime/arith/sub32.asm diff --git a/src/lib/arch/zx48k/runtime/subf.asm b/src/lib/arch/zxnext/runtime/arith/subf.asm similarity index 99% rename from src/lib/arch/zx48k/runtime/subf.asm rename to src/lib/arch/zxnext/runtime/arith/subf.asm index 1f1482f91..e59d87180 100644 --- a/src/lib/arch/zx48k/runtime/subf.asm +++ b/src/lib/arch/zxnext/runtime/arith/subf.asm @@ -25,4 +25,3 @@ __SUBF: ; Subtraction jp __FPSTACK_POP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/array.asm b/src/lib/arch/zxnext/runtime/array.asm index b801186cf..154b65c4d 100644 --- a/src/lib/arch/zxnext/runtime/array.asm +++ b/src/lib/arch/zxnext/runtime/array.asm @@ -16,7 +16,7 @@ ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... -#include once +#include once #ifdef __CHECK_ARRAY_BOUNDARY__ #include once diff --git a/src/lib/arch/zxnext/runtime/lei32.asm b/src/lib/arch/zxnext/runtime/lei32.asm index a53db51ba..c79c094b6 100644 --- a/src/lib/arch/zxnext/runtime/lei32.asm +++ b/src/lib/arch/zxnext/runtime/lei32.asm @@ -1,5 +1,5 @@ -#include once +#include once push namespace core diff --git a/src/lib/arch/zxnext/runtime/lti32.asm b/src/lib/arch/zxnext/runtime/lti32.asm index f49ce48b9..2f3e100a4 100644 --- a/src/lib/arch/zxnext/runtime/lti32.asm +++ b/src/lib/arch/zxnext/runtime/lti32.asm @@ -1,5 +1,5 @@ -#include once +#include once push namespace core diff --git a/src/lib/arch/zxnext/runtime/printi16.asm b/src/lib/arch/zxnext/runtime/printi16.asm index 6eadfb7e7..d44b22cbf 100644 --- a/src/lib/arch/zxnext/runtime/printi16.asm +++ b/src/lib/arch/zxnext/runtime/printi16.asm @@ -1,5 +1,5 @@ #include once -#include once +#include once #include once #include once @@ -40,4 +40,3 @@ __PRINTU_LOOP: ENDP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/printi32.asm b/src/lib/arch/zxnext/runtime/printi32.asm index e619fa492..eba8e215a 100644 --- a/src/lib/arch/zxnext/runtime/printi32.asm +++ b/src/lib/arch/zxnext/runtime/printi32.asm @@ -1,6 +1,6 @@ #include once #include once -#include once +#include once #include once @@ -48,4 +48,3 @@ __PRINTU_LOOP: ENDP pop namespace - diff --git a/src/lib/arch/zxnext/runtime/printi8.asm b/src/lib/arch/zxnext/runtime/printi8.asm index dcfaaeb9e..a12ecbfad 100644 --- a/src/lib/arch/zxnext/runtime/printi8.asm +++ b/src/lib/arch/zxnext/runtime/printi8.asm @@ -1,5 +1,5 @@ #include once -#include once +#include once push namespace core @@ -39,4 +39,3 @@ __PRINTU_LOOP: ENDP pop namespace - diff --git a/tests/functional/arch/zx48k/coercion1.asm b/tests/functional/arch/zx48k/coercion1.asm index 289a52603..03fa38611 100644 --- a/tests/functional/arch/zx48k/coercion1.asm +++ b/tests/functional/arch/zx48k/coercion1.asm @@ -82,8 +82,8 @@ _c: ei ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/addf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- @@ -122,7 +122,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/addf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -140,17 +140,9 @@ __ADDF: ; Addition defb 38h; ; END CALC jp __FPSTACK_POP pop namespace -#line 58 "coercion1.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/border.asm" - ; __FASTCALL__ Routine to change de border - ; Parameter (color) specified in A register - push namespace core - BORDER EQU 229Bh - pop namespace - ; Nothing to do! (Directly from the ZX Spectrum ROM) -#line 59 "coercion1.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/divf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/error.asm" +#line 58 "arch/zx48k/coercion1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/error.asm" ; Simple error control routines ; vim:ts=4:et: push namespace core @@ -184,7 +176,7 @@ __STOP: ld (ERR_NR), a ret pop namespace -#line 3 "/zxbasic/src/arch/zx48k/library-asm/divf.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses C EDHL registers as 1st paramter. @@ -231,9 +223,83 @@ __DIVBYZERO: ret ENDP pop namespace -#line 60 "coercion1.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/ftou32reg.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/neg32.asm" +#line 59 "arch/zx48k/coercion1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul8.asm" + push namespace core +__MUL8: ; Performs 8bit x 8bit multiplication + PROC + ;LOCAL __MUL8A + LOCAL __MUL8LOOP + LOCAL __MUL8B + ; 1st operand (byte) in A, 2nd operand into the stack (AF) + pop hl ; return address + ex (sp), hl ; CALLE convention +;;__MUL8_FAST: ; __FASTCALL__ entry + ;; ld e, a + ;; ld d, 0 + ;; ld l, d + ;; + ;; sla h + ;; jr nc, __MUL8A + ;; ld l, e + ;; +;;__MUL8A: + ;; + ;; ld b, 7 +;;__MUL8LOOP: + ;; add hl, hl + ;; jr nc, __MUL8B + ;; + ;; add hl, de + ;; +;;__MUL8B: + ;; djnz __MUL8LOOP + ;; + ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) +__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry + ld b, 8 + ld l, a + xor a +__MUL8LOOP: + add a, a ; a *= 2 + sla l + jp nc, __MUL8B + add a, h +__MUL8B: + djnz __MUL8LOOP + ret ; result = HL + ENDP + pop namespace +#line 60 "arch/zx48k/coercion1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 61 "arch/zx48k/coercion1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/border.asm" + ; __FASTCALL__ Routine to change de border + ; Parameter (color) specified in A register + push namespace core + BORDER EQU 229Bh + pop namespace + ; Nothing to do! (Directly from the ZX Spectrum ROM) +#line 62 "arch/zx48k/coercion1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" push namespace core __ABS32: bit 7, d @@ -258,7 +324,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/ftou32reg.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" push namespace core __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) ; Input FP number in A EDCB (A exponent, EDCB mantissa) @@ -330,74 +396,8 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ld a, l ret pop namespace -#line 61 "coercion1.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/mul8.asm" - push namespace core -__MUL8: ; Performs 8bit x 8bit multiplication - PROC - ;LOCAL __MUL8A - LOCAL __MUL8LOOP - LOCAL __MUL8B - ; 1st operand (byte) in A, 2nd operand into the stack (AF) - pop hl ; return address - ex (sp), hl ; CALLE convention -;;__MUL8_FAST: ; __FASTCALL__ entry - ;; ld e, a - ;; ld d, 0 - ;; ld l, d - ;; - ;; sla h - ;; jr nc, __MUL8A - ;; ld l, e - ;; -;;__MUL8A: - ;; - ;; ld b, 7 -;;__MUL8LOOP: - ;; add hl, hl - ;; jr nc, __MUL8B - ;; - ;; add hl, de - ;; -;;__MUL8B: - ;; djnz __MUL8LOOP - ;; - ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) -__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - ld b, 8 - ld l, a - xor a -__MUL8LOOP: - add a, a ; a *= 2 - sla l - jp nc, __MUL8B - add a, h -__MUL8B: - djnz __MUL8LOOP - ret ; result = HL - ENDP - pop namespace -#line 62 "coercion1.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace -#line 63 "coercion1.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/pushf.asm" +#line 63 "arch/zx48k/coercion1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pushf.asm" ; Routine to push Float pointed by HL ; Into the stack. Notice that the hl points to the last ; byte of the FP number. @@ -424,5 +424,5 @@ __FP_PUSH_REV: exx ret pop namespace -#line 64 "coercion1.bas" +#line 64 "arch/zx48k/coercion1.bas" END diff --git a/tests/functional/arch/zx48k/fact.asm b/tests/functional/arch/zx48k/fact.asm index eb7fbb93e..c52d31b9d 100644 --- a/tests/functional/arch/zx48k/fact.asm +++ b/tests/functional/arch/zx48k/fact.asm @@ -136,6 +136,117 @@ _fact__leave: DEFB 3Dh DEFB 20h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" + push namespace core +__MUL32: + ; multiplies 32 bit un/signed integer. + ; First operand stored in DEHL, and 2nd onto stack + ; Lowest part of 2nd operand on top of the stack + ; returns the result in DE.HL + exx + pop hl ; Return ADDRESS + pop de ; Low part + ex (sp), hl ; CALLEE -> HL = High part + ex de, hl + call __MUL32_64START +__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) + exx + push bc + exx + pop de + ld h, a + ld l, c + ret + pop namespace + f +#line 110 "arch/zx48k/fact.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 111 "arch/zx48k/fact.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cls.asm" ;; Clears the user screen (24 rows) #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sysvars.asm" @@ -193,7 +304,7 @@ CLS: ret ENDP pop namespace -#line 110 "arch/zx48k/fact.bas" +#line 112 "arch/zx48k/fact.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1080,90 +1191,7 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 111 "arch/zx48k/fact.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul32.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/_mul32.asm" -; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 - ; Used with permission. - ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') - ; 64bit result is returned in H'L'H L B'C'A C - push namespace core -__MUL32_64START: - push hl - exx - ld b, h - ld c, l ; BC = Low Part (A) - pop hl ; HL = Load Part (B) - ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') - push hl - exx - pop bc ; B'C' = HightPart(A) - exx ; A = B'C'BC , B = D'E'DE - ; multiply routine 32 * 32bit = 64bit - ; h'l'hlb'c'ac = b'c'bc * d'e'de - ; needs register a, changes flags - ; - ; this routine was with tiny differences in the - ; sinclair zx81 rom for the mantissa multiply -__LMUL: - xor a ; reset carry flag - ld h, a ; result bits 32..47 = 0 - ld l, a - exx - ld h, a ; result bits 48..63 = 0 - ld l, a - exx - ld a,b ; mpr is b'c'ac - ld b,33 ; initialize loop counter - jp __LMULSTART -__LMULLOOP: - jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP - ; it can save up to 33 * 2 = 66 cycles - ; But JR if 3 cycles faster if JUMP not taken! - add hl,de ; result += mpd - exx - adc hl,de - exx -__LMULNOADD: - exx - rr h ; right shift upper - rr l ; 32bit of result - exx - rr h - rr l -__LMULSTART: - exx - rr b ; right shift mpr/ - rr c ; lower 32bit of result - exx - rra ; equivalent to rr a - rr c - djnz __LMULLOOP - ret ; result in h'l'hlb'c'ac - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/mul32.asm" - push namespace core -__MUL32: - ; multiplies 32 bit un/signed integer. - ; First operand stored in DEHL, and 2nd onto stack - ; Lowest part of 2nd operand on top of the stack - ; returns the result in DE.HL - exx - pop hl ; Return ADDRESS - pop de ; Low part - ex (sp), hl ; CALLEE -> HL = High part - ex de, hl - call __MUL32_64START -__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) - exx - push bc - exx - pop de - ld h, a - ld l, c - ret - pop namespace -#line 112 "arch/zx48k/fact.bas" +#line 113 "arch/zx48k/fact.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printstr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" ; vim: ts=4:et:sw=4: @@ -1474,7 +1502,7 @@ __PRINT_STR: jp __PRINT_STR_LOOP ENDP pop namespace -#line 114 "arch/zx48k/fact.bas" +#line 115 "arch/zx48k/fact.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu32.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printnum.asm" @@ -1527,7 +1555,7 @@ __NEG32: ; Negates DEHL (Two's complement) ret pop namespace #line 3 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -1675,10 +1703,10 @@ __PRINTU_LOOP: ENDP pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printu32.asm" -#line 115 "arch/zx48k/fact.bas" +#line 116 "arch/zx48k/fact.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu8.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi8.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -1773,32 +1801,5 @@ __PRINTU_LOOP: ENDP pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printu8.asm" -#line 116 "arch/zx48k/fact.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sub32.asm" - ; SUB32 - ; Perform TOP of the stack - DEHL - ; Pops operand out of the stack (CALLEE) - ; and returns result in DEHL. Carry an Z are set correctly - push namespace core -__SUB32: - exx - pop bc ; saves return address in BC' - exx - or a ; clears carry flag - ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC - ld c, l - pop hl - sbc hl, bc - ex de, hl - ld b, h ; High part (DE) now in HL. Repeat operation - ld c, l - pop hl - sbc hl, bc - ex de, hl ; DEHL now has de 32 bit result - exx - push bc ; puts return address back - exx - ret - pop namespace #line 117 "arch/zx48k/fact.bas" END diff --git a/tests/functional/arch/zx48k/fporder.asm b/tests/functional/arch/zx48k/fporder.asm index 2712be491..632e94734 100644 --- a/tests/functional/arch/zx48k/fporder.asm +++ b/tests/functional/arch/zx48k/fporder.asm @@ -57,8 +57,8 @@ _n: ei ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/cos.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- @@ -97,17 +97,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/cos.asm" - push namespace core -COS: ; Computes COS using ROM FP-CALC - call __FPSTACK_PUSH - rst 28h ; ROM CALC - defb 20h ; COS - defb 38h ; END CALC - jp __FPSTACK_POP - pop namespace -#line 33 "fporder.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/mulf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -125,8 +115,38 @@ __MULF: ; Multiplication defb 38h; ; END CALC jp __FPSTACK_POP pop namespace -#line 34 "fporder.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/pushf.asm" +#line 33 "arch/zx48k/fporder.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__SUBF: ; Subtraction + call __FPSTACK_PUSH2 ; ENTERS B, A + ; ------------- ROM SUB + rst 28h + defb 01h ; EXCHANGE + defb 03h ; SUB + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 34 "arch/zx48k/fporder.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/cos.asm" + push namespace core +COS: ; Computes COS using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 20h ; COS + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 35 "arch/zx48k/fporder.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pushf.asm" ; Routine to push Float pointed by HL ; Into the stack. Notice that the hl points to the last ; byte of the FP number. @@ -153,8 +173,8 @@ __FP_PUSH_REV: exx ret pop namespace -#line 35 "fporder.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/storef.asm" +#line 36 "arch/zx48k/fporder.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storef.asm" push namespace core __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) push de @@ -182,25 +202,5 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL ld (hl), b ret pop namespace -#line 36 "fporder.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__SUBF: ; Subtraction - call __FPSTACK_PUSH2 ; ENTERS B, A - ; ------------- ROM SUB - rst 28h - defb 01h ; EXCHANGE - defb 03h ; SUB - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace -#line 37 "fporder.bas" +#line 37 "arch/zx48k/fporder.bas" END diff --git a/tests/functional/arch/zx48k/ifcrash.asm b/tests/functional/arch/zx48k/ifcrash.asm index a8146d9e0..366e8152b 100644 --- a/tests/functional/arch/zx48k/ifcrash.asm +++ b/tests/functional/arch/zx48k/ifcrash.asm @@ -118,9 +118,69 @@ _a: ei ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/eqf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/u32tofreg.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/neg32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__SUBF: ; Subtraction + call __FPSTACK_PUSH2 ; ENTERS B, A + ; ------------- ROM SUB + rst 28h + defb 01h ; EXCHANGE + defb 03h ; SUB + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 88 "arch/zx48k/ifcrash.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/eqf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/u32tofreg.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" push namespace core __ABS32: bit 7, d @@ -145,7 +205,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/u32tofreg.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/u32tofreg.asm" push namespace core __I8TOFREG: ld l, a @@ -215,8 +275,8 @@ __U32TOFREG_END: ret ENDP pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/eqf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/ftou32reg.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/eqf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" push namespace core __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) ; Input FP number in A EDCB (A exponent, EDCB mantissa) @@ -288,47 +348,7 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ld a, l ret pop namespace -#line 3 "/zxbasic/src/arch/zx48k/library-asm/eqf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 4 "/zxbasic/src/arch/zx48k/library-asm/eqf.asm" +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/eqf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses C EDHL registers as 1st paramter. @@ -348,8 +368,8 @@ __EQF: ; A = B call __FPSTACK_POP jp __FTOU8 ; Convert to 8 bits pop namespace -#line 88 "ifcrash.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/gtf.asm" +#line 89 "arch/zx48k/ifcrash.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/gtf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -369,8 +389,8 @@ __GTF: ; A > B call __FPSTACK_POP jp __FTOU8; Convert to 8 bits pop namespace -#line 90 "ifcrash.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/pushf.asm" +#line 91 "arch/zx48k/ifcrash.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pushf.asm" ; Routine to push Float pointed by HL ; Into the stack. Notice that the hl points to the last ; byte of the FP number. @@ -397,8 +417,8 @@ __FP_PUSH_REV: exx ret pop namespace -#line 91 "ifcrash.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/storef.asm" +#line 92 "arch/zx48k/ifcrash.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storef.asm" push namespace core __PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) push de @@ -426,25 +446,5 @@ __STOREF: ; Stores the given FP number in A EDCB at address HL ld (hl), b ret pop namespace -#line 92 "ifcrash.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__SUBF: ; Subtraction - call __FPSTACK_PUSH2 ; ENTERS B, A - ; ------------- ROM SUB - rst 28h - defb 01h ; EXCHANGE - defb 03h ; SUB - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace -#line 93 "ifcrash.bas" +#line 93 "arch/zx48k/ifcrash.bas" END diff --git a/tests/functional/arch/zx48k/loadu16ii.asm b/tests/functional/arch/zx48k/loadu16ii.asm index d2285eca9..d695577c2 100644 --- a/tests/functional/arch/zx48k/loadu16ii.asm +++ b/tests/functional/arch/zx48k/loadu16ii.asm @@ -63,6 +63,33 @@ ld c, l jp .core.__END_PROGRAM ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + LOCAL __MUL16LOOP + LOCAL __MUL16NOADD + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +__MUL16LOOP: + add hl, hl ; hl << 1 + sla c + rla ; a,c << 1 + jp nc, __MUL16NOADD + add hl, de +__MUL16NOADD: + djnz __MUL16LOOP + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 15 "arch/zx48k/loadu16ii.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -976,33 +1003,6 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 15 "arch/zx48k/loadu16ii.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul16.asm" - push namespace core -__MUL16: ; Mutiplies HL with the last value stored into de stack - ; Works for both signed and unsigned - PROC - LOCAL __MUL16LOOP - LOCAL __MUL16NOADD - ex de, hl - pop hl ; Return address - ex (sp), hl ; CALLEE caller convention -__MUL16_FAST: - ld b, 16 - ld a, h - ld c, l - ld hl, 0 -__MUL16LOOP: - add hl, hl ; hl << 1 - sla c - rla ; a,c << 1 - jp nc, __MUL16NOADD - add hl, de -__MUL16NOADD: - djnz __MUL16LOOP - ret ; Result in hl (16 lower bits) - ENDP - pop namespace #line 16 "arch/zx48k/loadu16ii.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu16.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi16.asm" @@ -1030,7 +1030,7 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printi16.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg16.asm" @@ -1049,7 +1049,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor diff --git a/tests/functional/arch/zx48k/ltee9.asm b/tests/functional/arch/zx48k/ltee9.asm index 6003df552..dbe9697da 100644 --- a/tests/functional/arch/zx48k/ltee9.asm +++ b/tests/functional/arch/zx48k/ltee9.asm @@ -104,10 +104,10 @@ _start__leave: pop ix ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/div16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values -#line 1 "/zxbasic/src/arch/zx48k/library-asm/neg16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg16.asm" ; Negates HL value (16 bit) push namespace core __ABS16: @@ -123,7 +123,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/arch/zx48k/library-asm/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor @@ -196,9 +196,9 @@ __MODI16: ; 16 bit modulus ; de = quotient ret pop namespace -#line 84 "ltee9.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/mul32.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/_mul32.asm" +#line 84 "arch/zx48k/ltee9.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" ; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 ; Used with permission. ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') @@ -257,7 +257,7 @@ __LMULSTART: djnz __LMULLOOP ret ; result in h'l'hlb'c'ac pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/mul32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" push namespace core __MUL32: ; multiplies 32 bit un/signed integer. @@ -279,9 +279,37 @@ __TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) ld l, c ret pop namespace -#line 85 "ltee9.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/pstore32.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/store32.asm" + f +#line 85 "arch/zx48k/ltee9.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 86 "arch/zx48k/ltee9.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pstore32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/store32.asm" push namespace core __PISTORE32: push hl @@ -304,7 +332,7 @@ __STORE32: ; Stores the given integer in DEBC at address HL ld (hl), d ret pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/pstore32.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/pstore32.asm" ; Stores a 32 bit integer number (DE,HL) at (IX + BC) push namespace core __PSTORE32: @@ -315,32 +343,5 @@ __PSTORE32: pop bc jp __STORE32 pop namespace -#line 86 "ltee9.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/sub32.asm" - ; SUB32 - ; Perform TOP of the stack - DEHL - ; Pops operand out of the stack (CALLEE) - ; and returns result in DEHL. Carry an Z are set correctly - push namespace core -__SUB32: - exx - pop bc ; saves return address in BC' - exx - or a ; clears carry flag - ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC - ld c, l - pop hl - sbc hl, bc - ex de, hl - ld b, h ; High part (DE) now in HL. Repeat operation - ld c, l - pop hl - sbc hl, bc - ex de, hl ; DEHL now has de 32 bit result - exx - push bc ; puts return address back - exx - ret - pop namespace -#line 87 "ltee9.bas" +#line 87 "arch/zx48k/ltee9.bas" END diff --git a/tests/functional/arch/zx48k/mcleod.asm b/tests/functional/arch/zx48k/mcleod.asm index a89faf486..a035f917a 100644 --- a/tests/functional/arch/zx48k/mcleod.asm +++ b/tests/functional/arch/zx48k/mcleod.asm @@ -48,8 +48,67 @@ _a: ei ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/ftou32reg.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/neg32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 28 "arch/zx48k/mcleod.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" push namespace core __ABS32: bit 7, d @@ -74,7 +133,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/ftou32reg.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/ftou32reg.asm" push namespace core __FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) ; Input FP number in A EDCB (A exponent, EDCB mantissa) @@ -146,67 +205,8 @@ __FTOU8: ; Converts float in C ED LH to Unsigned byte in A ld a, l ret pop namespace -#line 28 "mcleod.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/mulf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace -#line 29 "mcleod.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/random.asm" +#line 29 "arch/zx48k/mcleod.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/random.asm" ; RANDOM functions push namespace core RANDOMIZE: @@ -306,5 +306,5 @@ RND_LOOP: ret ENDP pop namespace -#line 30 "mcleod.bas" +#line 30 "arch/zx48k/mcleod.bas" END diff --git a/tests/functional/arch/zx48k/opt3_data2.asm b/tests/functional/arch/zx48k/opt3_data2.asm index f97877abc..52f681f72 100644 --- a/tests/functional/arch/zx48k/opt3_data2.asm +++ b/tests/functional/arch/zx48k/opt3_data2.asm @@ -143,6 +143,53 @@ ___DATA__FUNCPTR__5__leave: __DATA__END: DEFB 00h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul8.asm" + push namespace core +__MUL8: ; Performs 8bit x 8bit multiplication + PROC + ;LOCAL __MUL8A + LOCAL __MUL8LOOP + LOCAL __MUL8B + ; 1st operand (byte) in A, 2nd operand into the stack (AF) + pop hl ; return address + ex (sp), hl ; CALLE convention +;;__MUL8_FAST: ; __FASTCALL__ entry + ;; ld e, a + ;; ld d, 0 + ;; ld l, d + ;; + ;; sla h + ;; jr nc, __MUL8A + ;; ld l, e + ;; +;;__MUL8A: + ;; + ;; ld b, 7 +;;__MUL8LOOP: + ;; add hl, hl + ;; jr nc, __MUL8B + ;; + ;; add hl, de + ;; +;;__MUL8B: + ;; djnz __MUL8LOOP + ;; + ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) +__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry + ld b, 8 + ld l, a + xor a +__MUL8LOOP: + add a, a ; a *= 2 + sla l + jp nc, __MUL8B + add a, h +__MUL8B: + djnz __MUL8LOOP + ret ; result = HL + ENDP + pop namespace +#line 97 "arch/zx48k/opt3_data2.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -159,7 +206,7 @@ __DATA__END: ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] ; What I will do here is to calculate the following sequence: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" push namespace core __MUL16: ; Mutiplies HL with the last value stored into de stack ; Works for both signed and unsigned @@ -299,7 +346,7 @@ __FNMUL2: ret ENDP pop namespace -#line 97 "arch/zx48k/opt3_data2.bas" +#line 98 "arch/zx48k/opt3_data2.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1213,53 +1260,6 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 98 "arch/zx48k/opt3_data2.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul8.asm" - push namespace core -__MUL8: ; Performs 8bit x 8bit multiplication - PROC - ;LOCAL __MUL8A - LOCAL __MUL8LOOP - LOCAL __MUL8B - ; 1st operand (byte) in A, 2nd operand into the stack (AF) - pop hl ; return address - ex (sp), hl ; CALLE convention -;;__MUL8_FAST: ; __FASTCALL__ entry - ;; ld e, a - ;; ld d, 0 - ;; ld l, d - ;; - ;; sla h - ;; jr nc, __MUL8A - ;; ld l, e - ;; -;;__MUL8A: - ;; - ;; ld b, 7 -;;__MUL8LOOP: - ;; add hl, hl - ;; jr nc, __MUL8B - ;; - ;; add hl, de - ;; -;;__MUL8B: - ;; djnz __MUL8LOOP - ;; - ;; ld a, l ; result = A and HL (Truncate to lower 8 bits) -__MUL8_FAST: ; __FASTCALL__ entry, a = a * h (8 bit mul) and Carry - ld b, 8 - ld l, a - xor a -__MUL8LOOP: - add a, a ; a *= 2 - sla l - jp nc, __MUL8B - add a, h -__MUL8B: - djnz __MUL8LOOP - ret ; result = HL - ENDP - pop namespace #line 99 "arch/zx48k/opt3_data2.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu8.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi8.asm" @@ -1287,7 +1287,7 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printi8.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division diff --git a/tests/functional/arch/zx48k/pooky0.asm b/tests/functional/arch/zx48k/pooky0.asm index 35d59a36d..1317776c7 100644 --- a/tests/functional/arch/zx48k/pooky0.asm +++ b/tests/functional/arch/zx48k/pooky0.asm @@ -158,8 +158,8 @@ __EXIT_FUNCTION: exx ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/addf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- @@ -198,7 +198,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/addf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -216,8 +216,8 @@ __ADDF: ; Addition defb 38h; ; END CALC jp __FPSTACK_POP pop namespace -#line 140 "pooky0.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/mulf.asm" +#line 140 "arch/zx48k/pooky0.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -235,13 +235,33 @@ __MULF: ; Multiplication defb 38h; ; END CALC jp __FPSTACK_POP pop namespace -#line 141 "pooky0.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/ploadf.asm" +#line 141 "arch/zx48k/pooky0.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__SUBF: ; Subtraction + call __FPSTACK_PUSH2 ; ENTERS B, A + ; ------------- ROM SUB + rst 28h + defb 01h ; EXCHANGE + defb 03h ; SUB + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 142 "arch/zx48k/pooky0.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ploadf.asm" ; Parameter / Local var load ; A => Offset ; IX = Stack Frame ; RESULT: HL => IX + DE -#line 1 "/zxbasic/src/arch/zx48k/library-asm/iloadf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/iloadf.asm" ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB ; stored at position pointed by POINTER HL @@ -268,7 +288,7 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL ld b, (hl) ret pop namespace -#line 7 "/zxbasic/src/arch/zx48k/library-asm/ploadf.asm" +#line 7 "/zxbasic/src/lib/arch/zx48k/runtime/ploadf.asm" push namespace core __PLOADF: push ix @@ -276,29 +296,9 @@ __PLOADF: add hl, de jp __LOADF pop namespace -#line 142 "pooky0.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__SUBF: ; Subtraction - call __FPSTACK_PUSH2 ; ENTERS B, A - ; ------------- ROM SUB - rst 28h - defb 01h ; EXCHANGE - defb 03h ; SUB - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace -#line 143 "pooky0.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/u32tofreg.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/neg32.asm" +#line 143 "arch/zx48k/pooky0.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/u32tofreg.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" push namespace core __ABS32: bit 7, d @@ -323,7 +323,7 @@ __NEG32: ; Negates DEHL (Two's complement) inc de ret pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/u32tofreg.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/u32tofreg.asm" push namespace core __I8TOFREG: ld l, a @@ -393,5 +393,5 @@ __U32TOFREG_END: ret ENDP pop namespace -#line 144 "pooky0.bas" +#line 144 "arch/zx48k/pooky0.bas" END diff --git a/tests/functional/arch/zx48k/read10.asm b/tests/functional/arch/zx48k/read10.asm index cfae2c5a1..d0bfa7676 100644 --- a/tests/functional/arch/zx48k/read10.asm +++ b/tests/functional/arch/zx48k/read10.asm @@ -146,6 +146,65 @@ ___DATA__FUNCPTR__2__leave: __DATA__END: DEFB 00h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 116 "arch/zx48k/read10.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1059,48 +1118,8 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 116 "arch/zx48k/read10.bas" +#line 117 "arch/zx48k/read10.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" push namespace core SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH @@ -1109,7 +1128,7 @@ SIN: ; Computes SIN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 117 "arch/zx48k/read10.bas" +#line 118 "arch/zx48k/read10.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" push namespace core TAN: ; Computes TAN using ROM FP-CALC @@ -1119,25 +1138,6 @@ TAN: ; Computes TAN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 118 "arch/zx48k/read10.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace #line 119 "arch/zx48k/read10.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/ploadf.asm" ; Parameter / Local var load diff --git a/tests/functional/arch/zx48k/read13.asm b/tests/functional/arch/zx48k/read13.asm index f8430f4ce..e8e3aafcc 100644 --- a/tests/functional/arch/zx48k/read13.asm +++ b/tests/functional/arch/zx48k/read13.asm @@ -168,6 +168,187 @@ ___DATA__FUNCPTR__2__leave: __DATA__END: DEFB 00h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 111 "arch/zx48k/read13.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf16.asm" + push namespace core +__MULF16: ; + ld a, d ; load sgn into a + ex af, af' ; saves it + call __ABS32 ; convert to positive + exx + pop hl ; Return address + pop de ; Low part + ex (sp), hl ; CALLEE caller convention; Now HL = Hight part, (SP) = Return address + ex de, hl ; D'E' = High part (B), H'L' = Low part (B) (must be in DE) + ex af, af' + xor d ; A register contains resulting sgn + ex af, af' + call __ABS32 ; convert to positive + call __MUL32_64START + ; rounding (was not included in zx81) +__ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 + ; result returned in dehl + ; input in h'l'hlb'c'ac + sla a ; result bit 47 to carry + exx + ld hl,0 ; ld does not change carry + adc hl,bc ; hl = hl + 0 + carry + push hl + exx + ld bc,0 + adc hl,bc ; hl = hl + 0 + carry + ex de, hl + pop hl ; rounded result in de.hl + ex af, af' ; recovers result sign + or a + jp m, __NEG32 ; if negative, negates it + ret + pop namespace +#line 112 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -184,7 +365,7 @@ __DATA__END: ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] ; What I will do here is to calculate the following sequence: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" push namespace core __MUL16: ; Mutiplies HL with the last value stored into de stack ; Works for both signed and unsigned @@ -324,7 +505,7 @@ __FNMUL2: ret ENDP pop namespace -#line 111 "arch/zx48k/read13.bas" +#line 113 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1238,34 +1419,8 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 112 "arch/zx48k/read13.bas" +#line 114 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/f16tofreg.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg32.asm" - push namespace core -__ABS32: - bit 7, d - ret z -__NEG32: ; Negates DEHL (Two's complement) - ld a, l - cpl - ld l, a - ld a, h - cpl - ld h, a - ld a, e - cpl - ld e, a - ld a, d - cpl - ld d, a - inc l - ret nz - inc h - ret nz - inc de - ret - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/f16tofreg.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/u32tofreg.asm" push namespace core __I8TOFREG: @@ -1371,7 +1526,7 @@ __F16TOFREG2: ; Converts an unsigned 32 bit integer (DEHL) jp __U32TOFREG_LOOP ; Proceed as an integer ENDP pop namespace -#line 113 "arch/zx48k/read13.bas" +#line 115 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/iload32.asm" ; __FASTCALL__ routine which ; loads a 32 bits integer into DE,HL @@ -1390,48 +1545,8 @@ __ILOAD32: ex de, hl ret pop namespace -#line 114 "arch/zx48k/read13.bas" +#line 116 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" push namespace core SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH @@ -1440,7 +1555,7 @@ SIN: ; Computes SIN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 115 "arch/zx48k/read13.bas" +#line 117 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" push namespace core TAN: ; Computes TAN using ROM FP-CALC @@ -1450,121 +1565,6 @@ TAN: ; Computes TAN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 116 "arch/zx48k/read13.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace -#line 117 "arch/zx48k/read13.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf16.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/_mul32.asm" -; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 - ; Used with permission. - ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') - ; 64bit result is returned in H'L'H L B'C'A C - push namespace core -__MUL32_64START: - push hl - exx - ld b, h - ld c, l ; BC = Low Part (A) - pop hl ; HL = Load Part (B) - ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') - push hl - exx - pop bc ; B'C' = HightPart(A) - exx ; A = B'C'BC , B = D'E'DE - ; multiply routine 32 * 32bit = 64bit - ; h'l'hlb'c'ac = b'c'bc * d'e'de - ; needs register a, changes flags - ; - ; this routine was with tiny differences in the - ; sinclair zx81 rom for the mantissa multiply -__LMUL: - xor a ; reset carry flag - ld h, a ; result bits 32..47 = 0 - ld l, a - exx - ld h, a ; result bits 48..63 = 0 - ld l, a - exx - ld a,b ; mpr is b'c'ac - ld b,33 ; initialize loop counter - jp __LMULSTART -__LMULLOOP: - jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP - ; it can save up to 33 * 2 = 66 cycles - ; But JR if 3 cycles faster if JUMP not taken! - add hl,de ; result += mpd - exx - adc hl,de - exx -__LMULNOADD: - exx - rr h ; right shift upper - rr l ; 32bit of result - exx - rr h - rr l -__LMULSTART: - exx - rr b ; right shift mpr/ - rr c ; lower 32bit of result - exx - rra ; equivalent to rr a - rr c - djnz __LMULLOOP - ret ; result in h'l'hlb'c'ac - pop namespace -#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/mulf16.asm" - push namespace core -__MULF16: ; - ld a, d ; load sgn into a - ex af, af' ; saves it - call __ABS32 ; convert to positive - exx - pop hl ; Return address - pop de ; Low part - ex (sp), hl ; CALLEE caller convention; Now HL = Hight part, (SP) = Return address - ex de, hl ; D'E' = High part (B), H'L' = Low part (B) (must be in DE) - ex af, af' - xor d ; A register contains resulting sgn - ex af, af' - call __ABS32 ; convert to positive - call __MUL32_64START - ; rounding (was not included in zx81) -__ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 - ; result returned in dehl - ; input in h'l'hlb'c'ac - sla a ; result bit 47 to carry - exx - ld hl,0 ; ld does not change carry - adc hl,bc ; hl = hl + 0 + carry - push hl - exx - ld bc,0 - adc hl,bc ; hl = hl + 0 + carry - ex de, hl - pop hl ; rounded result in de.hl - ex af, af' ; recovers result sign - or a - jp m, __NEG32 ; if negative, negates it - ret - pop namespace #line 118 "arch/zx48k/read13.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pow.asm" ; ------------------------------------------------------------- @@ -1618,7 +1618,7 @@ __PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printf16.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi16.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" ; 16 bit division and modulo functions ; for both signed and unsigned values #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/neg16.asm" @@ -1637,7 +1637,7 @@ __NEGHL: inc hl ret pop namespace -#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/div16.asm" +#line 5 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div16.asm" push namespace core __DIVU16: ; 16 bit unsigned division ; HL = Dividend, Stack Top = Divisor diff --git a/tests/functional/arch/zx48k/read4.asm b/tests/functional/arch/zx48k/read4.asm index 46e13b079..03d5bef4a 100644 --- a/tests/functional/arch/zx48k/read4.asm +++ b/tests/functional/arch/zx48k/read4.asm @@ -153,6 +153,65 @@ __DATA__END: DEFB 6Ch DEFB 6Fh ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 89 "arch/zx48k/read4.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/loadstr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/alloc.asm" ; vim: ts=4:et:sw=4: @@ -491,48 +550,8 @@ __LOADSTR: ; __FASTCALL__ entry pop hl ; Recovers destiny in hl as result ret pop namespace -#line 89 "arch/zx48k/read4.bas" +#line 90 "arch/zx48k/read4.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" push namespace core SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH @@ -541,7 +560,7 @@ SIN: ; Computes SIN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 90 "arch/zx48k/read4.bas" +#line 91 "arch/zx48k/read4.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" push namespace core TAN: ; Computes TAN using ROM FP-CALC @@ -551,25 +570,6 @@ TAN: ; Computes TAN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 91 "arch/zx48k/read4.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace #line 92 "arch/zx48k/read4.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pow.asm" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zx48k/read5.asm b/tests/functional/arch/zx48k/read5.asm index a45c7e0c8..71b8f1f80 100644 --- a/tests/functional/arch/zx48k/read5.asm +++ b/tests/functional/arch/zx48k/read5.asm @@ -146,6 +146,65 @@ ___DATA__FUNCPTR__2__leave: __DATA__END: DEFB 00h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 110 "arch/zx48k/read5.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1059,48 +1118,8 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 110 "arch/zx48k/read5.bas" +#line 111 "arch/zx48k/read5.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" push namespace core SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH @@ -1109,7 +1128,7 @@ SIN: ; Computes SIN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 111 "arch/zx48k/read5.bas" +#line 112 "arch/zx48k/read5.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" push namespace core TAN: ; Computes TAN using ROM FP-CALC @@ -1119,25 +1138,6 @@ TAN: ; Computes TAN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 112 "arch/zx48k/read5.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace #line 113 "arch/zx48k/read5.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pow.asm" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zx48k/read8.asm b/tests/functional/arch/zx48k/read8.asm index 258ff5c06..44934b41d 100644 --- a/tests/functional/arch/zx48k/read8.asm +++ b/tests/functional/arch/zx48k/read8.asm @@ -163,6 +163,65 @@ ___DATA__FUNCPTR__2__leave: __DATA__END: DEFB 00h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 101 "arch/zx48k/read8.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1076,48 +1135,8 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 101 "arch/zx48k/read8.bas" +#line 102 "arch/zx48k/read8.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" push namespace core SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH @@ -1126,7 +1145,7 @@ SIN: ; Computes SIN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 102 "arch/zx48k/read8.bas" +#line 103 "arch/zx48k/read8.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" push namespace core TAN: ; Computes TAN using ROM FP-CALC @@ -1136,25 +1155,6 @@ TAN: ; Computes TAN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 103 "arch/zx48k/read8.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace #line 104 "arch/zx48k/read8.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pow.asm" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zx48k/read9.asm b/tests/functional/arch/zx48k/read9.asm index 4dbd6b410..7afc5c3c9 100644 --- a/tests/functional/arch/zx48k/read9.asm +++ b/tests/functional/arch/zx48k/read9.asm @@ -178,6 +178,65 @@ ___DATA__FUNCPTR__2__leave: __DATA__END: DEFB 00h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 116 "arch/zx48k/read9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array.asm" ; vim: ts=4:et:sw=4: ; Copyleft (K) by Jose M. Rodriguez de la Rosa @@ -194,7 +253,7 @@ __DATA__END: ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] ; What I will do here is to calculate the following sequence: ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul16.asm" push namespace core __MUL16: ; Mutiplies HL with the last value stored into de stack ; Works for both signed and unsigned @@ -334,7 +393,7 @@ __FNMUL2: ret ENDP pop namespace -#line 116 "arch/zx48k/read9.bas" +#line 117 "arch/zx48k/read9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1248,7 +1307,7 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 117 "arch/zx48k/read9.bas" +#line 118 "arch/zx48k/read9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/iloadf.asm" ; __FASTCALL__ routine which ; loads a 40 bits floating point into A ED CB @@ -1276,48 +1335,8 @@ __LOADF: ; Loads a 40 bits FP number from address pointed by HL ld b, (hl) ret pop namespace -#line 118 "arch/zx48k/read9.bas" +#line 119 "arch/zx48k/read9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" - ; ------------------------------------------------------------- - ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC - ; ------------------------------------------------------------- - push namespace core - __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) - __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) -__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) - ; Second argument to push into the stack calculator is popped out of the stack - ; Since the caller routine also receives the parameters into the top of the stack - ; four bytes must be removed from SP before pop them out - call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK - exx - pop hl ; Caller-Caller return addr - exx - pop hl ; Caller return addr - pop af - pop de - pop bc - push hl ; Caller return addr - exx - push hl ; Caller-Caller return addr - exx - jp __FPSTACK_PUSH -__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK - ; This format is specified in the ZX 48K Manual - ; You can push a 16 bit signed integer as - ; 0 SS LL HH 0, being SS the sign and LL HH the low - ; and High byte respectively - ld a, h - rla ; sign to Carry - sbc a, a ; 0 if positive, FF if negative - ld e, a - ld d, l - ld c, h - xor a - ld b, a - jp __FPSTACK_PUSH - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" push namespace core SIN: ; Computes SIN using ROM FP-CALC call __FPSTACK_PUSH @@ -1326,7 +1345,7 @@ SIN: ; Computes SIN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 119 "arch/zx48k/read9.bas" +#line 120 "arch/zx48k/read9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" push namespace core TAN: ; Computes TAN using ROM FP-CALC @@ -1336,25 +1355,6 @@ TAN: ; Computes TAN using ROM FP-CALC defb 38h ; END CALC jp __FPSTACK_POP pop namespace -#line 120 "arch/zx48k/read9.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mulf.asm" - ; ------------------------------------------------------------- - ; Floating point library using the FP ROM Calculator (ZX 48K) - ; All of them uses A EDCB registers as 1st paramter. - ; For binary operators, the 2n operator must be pushed into the - ; stack, in the order A DE BC. - ; - ; Uses CALLEE convention - ; ------------------------------------------------------------- - push namespace core -__MULF: ; Multiplication - call __FPSTACK_PUSH2 - ; ------------- ROM MUL - rst 28h - defb 04h ; - defb 38h; ; END CALC - jp __FPSTACK_POP - pop namespace #line 121 "arch/zx48k/read9.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pow.asm" ; ------------------------------------------------------------- diff --git a/tests/functional/arch/zx48k/subf00.asm b/tests/functional/arch/zx48k/subf00.asm index 1dfd7615a..fb0546ba4 100644 --- a/tests/functional/arch/zx48k/subf00.asm +++ b/tests/functional/arch/zx48k/subf00.asm @@ -55,65 +55,8 @@ _b: ei ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/pushf.asm" - ; Routine to push Float pointed by HL - ; Into the stack. Notice that the hl points to the last - ; byte of the FP number. - ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers - push namespace core -__FP_PUSH_REV: - push hl - exx - pop hl - pop bc ; Return Address - ld d, (hl) - dec hl - ld e, (hl) - dec hl - push de - ld d, (hl) - dec hl - ld e, (hl) - dec hl - push de - ld d, (hl) - push de - push bc ; Return Address - exx - ret - pop namespace -#line 25 "subf00.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/storef.asm" - push namespace core -__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) - push de - ex de, hl ; DE <- HL - push ix - pop hl ; HL <- IX - add hl, de ; HL <- IX + HL - pop de -__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register - ex af, af' - ld a, (hl) - inc hl - ld h, (hl) - ld l, a ; HL = (HL) - ex af, af' -__STOREF: ; Stores the given FP number in A EDCB at address HL - ld (hl), a - inc hl - ld (hl), e - inc hl - ld (hl), d - inc hl - ld (hl), c - inc hl - ld (hl), b - ret - pop namespace -#line 26 "subf00.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- @@ -152,7 +95,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -171,5 +114,62 @@ __SUBF: ; Subtraction defb 38h; ; END CALC jp __FPSTACK_POP pop namespace -#line 27 "subf00.bas" +#line 25 "arch/zx48k/subf00.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pushf.asm" + ; Routine to push Float pointed by HL + ; Into the stack. Notice that the hl points to the last + ; byte of the FP number. + ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers + push namespace core +__FP_PUSH_REV: + push hl + exx + pop hl + pop bc ; Return Address + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + push de + push bc ; Return Address + exx + ret + pop namespace +#line 26 "arch/zx48k/subf00.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 27 "arch/zx48k/subf00.bas" END diff --git a/tests/functional/arch/zx48k/subf01.asm b/tests/functional/arch/zx48k/subf01.asm index 0b120582e..14ccddbf4 100644 --- a/tests/functional/arch/zx48k/subf01.asm +++ b/tests/functional/arch/zx48k/subf01.asm @@ -59,37 +59,8 @@ _b: ei ret ;; --- end of user code --- -#line 1 "/zxbasic/src/arch/zx48k/library-asm/storef.asm" - push namespace core -__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) - push de - ex de, hl ; DE <- HL - push ix - pop hl ; HL <- IX - add hl, de ; HL <- IX + HL - pop de -__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register - ex af, af' - ld a, (hl) - inc hl - ld h, (hl) - ld l, a ; HL = (HL) - ex af, af' -__STOREF: ; Stores the given FP number in A EDCB at address HL - ld (hl), a - inc hl - ld (hl), e - inc hl - ld (hl), d - inc hl - ld (hl), c - inc hl - ld (hl), b - ret - pop namespace -#line 29 "subf01.bas" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" -#line 1 "/zxbasic/src/arch/zx48k/library-asm/stackf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/stackf.asm" ; ------------------------------------------------------------- ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC ; ------------------------------------------------------------- @@ -128,7 +99,7 @@ __FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK ld b, a jp __FPSTACK_PUSH pop namespace -#line 2 "/zxbasic/src/arch/zx48k/library-asm/subf.asm" +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" ; ------------------------------------------------------------- ; Floating point library using the FP ROM Calculator (ZX 48K) ; All of them uses A EDCB registers as 1st paramter. @@ -147,5 +118,34 @@ __SUBF: ; Subtraction defb 38h; ; END CALC jp __FPSTACK_POP pop namespace -#line 30 "subf01.bas" +#line 29 "arch/zx48k/subf01.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 30 "arch/zx48k/subf01.bas" END diff --git a/tests/functional/arch/zx48k/subrec.asm b/tests/functional/arch/zx48k/subrec.asm index 3e042ff99..e78b2c5d9 100644 --- a/tests/functional/arch/zx48k/subrec.asm +++ b/tests/functional/arch/zx48k/subrec.asm @@ -148,6 +148,117 @@ _fact__leave: DEFB 3Dh DEFB 20h ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mul32.asm" + push namespace core +__MUL32: + ; multiplies 32 bit un/signed integer. + ; First operand stored in DEHL, and 2nd onto stack + ; Lowest part of 2nd operand on top of the stack + ; returns the result in DE.HL + exx + pop hl ; Return ADDRESS + pop de ; Low part + ex (sp), hl ; CALLEE -> HL = High part + ex de, hl + call __MUL32_64START +__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) + exx + push bc + exx + pop de + ld h, a + ld l, c + ret + pop namespace + f +#line 117 "arch/zx48k/subrec.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 118 "arch/zx48k/subrec.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cls.asm" ;; Clears the user screen (24 rows) #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sysvars.asm" @@ -205,7 +316,7 @@ CLS: ret ENDP pop namespace -#line 117 "arch/zx48k/subrec.bas" +#line 119 "arch/zx48k/subrec.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/print.asm" ; vim:ts=4:sw=4:et: @@ -1092,90 +1203,7 @@ __REFRESH_TMP: ret ENDP pop namespace -#line 118 "arch/zx48k/subrec.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mul32.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/_mul32.asm" -; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 - ; Used with permission. - ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') - ; 64bit result is returned in H'L'H L B'C'A C - push namespace core -__MUL32_64START: - push hl - exx - ld b, h - ld c, l ; BC = Low Part (A) - pop hl ; HL = Load Part (B) - ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') - push hl - exx - pop bc ; B'C' = HightPart(A) - exx ; A = B'C'BC , B = D'E'DE - ; multiply routine 32 * 32bit = 64bit - ; h'l'hlb'c'ac = b'c'bc * d'e'de - ; needs register a, changes flags - ; - ; this routine was with tiny differences in the - ; sinclair zx81 rom for the mantissa multiply -__LMUL: - xor a ; reset carry flag - ld h, a ; result bits 32..47 = 0 - ld l, a - exx - ld h, a ; result bits 48..63 = 0 - ld l, a - exx - ld a,b ; mpr is b'c'ac - ld b,33 ; initialize loop counter - jp __LMULSTART -__LMULLOOP: - jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP - ; it can save up to 33 * 2 = 66 cycles - ; But JR if 3 cycles faster if JUMP not taken! - add hl,de ; result += mpd - exx - adc hl,de - exx -__LMULNOADD: - exx - rr h ; right shift upper - rr l ; 32bit of result - exx - rr h - rr l -__LMULSTART: - exx - rr b ; right shift mpr/ - rr c ; lower 32bit of result - exx - rra ; equivalent to rr a - rr c - djnz __LMULLOOP - ret ; result in h'l'hlb'c'ac - pop namespace -#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/mul32.asm" - push namespace core -__MUL32: - ; multiplies 32 bit un/signed integer. - ; First operand stored in DEHL, and 2nd onto stack - ; Lowest part of 2nd operand on top of the stack - ; returns the result in DE.HL - exx - pop hl ; Return ADDRESS - pop de ; Low part - ex (sp), hl ; CALLEE -> HL = High part - ex de, hl - call __MUL32_64START -__TO32BIT: ; Converts H'L'HLB'C'AC to DEHL (Discards H'L'HL) - exx - push bc - exx - pop de - ld h, a - ld l, c - ret - pop namespace -#line 119 "arch/zx48k/subrec.bas" +#line 120 "arch/zx48k/subrec.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printstr.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/free.asm" ; vim: ts=4:et:sw=4: @@ -1486,7 +1514,7 @@ __PRINT_STR: jp __PRINT_STR_LOOP ENDP pop namespace -#line 121 "arch/zx48k/subrec.bas" +#line 122 "arch/zx48k/subrec.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu32.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printnum.asm" @@ -1539,7 +1567,7 @@ __NEG32: ; Negates DEHL (Two's complement) ret pop namespace #line 3 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.asm" ; --------------------------------------------------------- push namespace core __DIVU32: ; 32 bit unsigned division @@ -1687,10 +1715,10 @@ __PRINTU_LOOP: ENDP pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printu32.asm" -#line 122 "arch/zx48k/subrec.bas" +#line 123 "arch/zx48k/subrec.bas" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu8.asm" #line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi8.asm" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/div8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div8.asm" ; -------------------------------- push namespace core __DIVU8: ; 8 bit unsigned integer division @@ -1785,32 +1813,5 @@ __PRINTU_LOOP: ENDP pop namespace #line 2 "/zxbasic/src/lib/arch/zx48k/runtime/printu8.asm" -#line 123 "arch/zx48k/subrec.bas" -#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sub32.asm" - ; SUB32 - ; Perform TOP of the stack - DEHL - ; Pops operand out of the stack (CALLEE) - ; and returns result in DEHL. Carry an Z are set correctly - push namespace core -__SUB32: - exx - pop bc ; saves return address in BC' - exx - or a ; clears carry flag - ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC - ld c, l - pop hl - sbc hl, bc - ex de, hl - ld b, h ; High part (DE) now in HL. Repeat operation - ld c, l - pop hl - sbc hl, bc - ex de, hl ; DEHL now has de 32 bit result - exx - push bc ; puts return address back - exx - ret - pop namespace #line 124 "arch/zx48k/subrec.bas" END diff --git a/tests/functional/arch/zxnext/add16.asm b/tests/functional/arch/zxnext/add16.asm new file mode 100644 index 000000000..504d68d12 --- /dev/null +++ b/tests/functional/arch/zxnext/add16.asm @@ -0,0 +1,49 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld (_b), hl + ld hl, (_a) + inc hl + ld (_b), hl + ld hl, (_a) + ld (_b), hl + ld hl, (_a) + inc hl + ld (_b), hl + ld de, (_a) + ld hl, (_a) + add hl, de + ld (_b), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add16.bas b/tests/functional/arch/zxnext/add16.bas new file mode 100644 index 000000000..f2273e9f2 --- /dev/null +++ b/tests/functional/arch/zxnext/add16.bas @@ -0,0 +1,10 @@ +' TEST for ADD16 + +DIM a as UInteger +DIM b as UInteger + +b = a + 0 +b = a + 1 +b = 0 + a +b = 1 + a +b = a + a diff --git a/tests/functional/arch/zxnext/add16a.asm b/tests/functional/arch/zxnext/add16a.asm new file mode 100644 index 000000000..129931b92 --- /dev/null +++ b/tests/functional/arch/zxnext/add16a.asm @@ -0,0 +1,40 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, (_a) + ld hl, (_a) + add hl, de + ex de, hl + ld hl, (_a) + add hl, de + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add16a.bas b/tests/functional/arch/zxnext/add16a.bas new file mode 100644 index 000000000..37b6bf698 --- /dev/null +++ b/tests/functional/arch/zxnext/add16a.bas @@ -0,0 +1,4 @@ + +REM another ADD16 tests +DIM a As Integer +a = a + a + a diff --git a/tests/functional/arch/zxnext/add16b.asm b/tests/functional/arch/zxnext/add16b.asm new file mode 100644 index 000000000..a25368df8 --- /dev/null +++ b/tests/functional/arch/zxnext/add16b.asm @@ -0,0 +1,47 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, (_a) + ld hl, (_a) + add hl, de + push hl + ld hl, (_a) + inc hl + inc hl + ex de, hl + ld hl, (_a) + add hl, de + ex de, hl + pop hl + add hl, de + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add16b.bas b/tests/functional/arch/zxnext/add16b.bas new file mode 100644 index 000000000..30b684b21 --- /dev/null +++ b/tests/functional/arch/zxnext/add16b.bas @@ -0,0 +1,4 @@ + +REM another ADD16 tests +DIM a As Integer +a = a + a + (a + 2 + a) diff --git a/tests/functional/arch/zxnext/add32.asm b/tests/functional/arch/zxnext/add32.asm new file mode 100644 index 000000000..fcd50b9a9 --- /dev/null +++ b/tests/functional/arch/zxnext/add32.asm @@ -0,0 +1,73 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +_b: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld de, 0 + ld hl, 1 + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld de, 0 + ld hl, 1 + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_b), hl + ld (_b + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add32.bas b/tests/functional/arch/zxnext/add32.bas new file mode 100644 index 000000000..db03bf26d --- /dev/null +++ b/tests/functional/arch/zxnext/add32.bas @@ -0,0 +1,10 @@ +' TEST for Ulong add (32 bit) + +DIM a as Ulong +DIM b as Ulong + +b = a + 0 +b = a + 1 +b = 0 + a +b = 1 + a +b = a + a diff --git a/tests/functional/arch/zxnext/add32a.asm b/tests/functional/arch/zxnext/add32a.asm new file mode 100644 index 000000000..d35f33022 --- /dev/null +++ b/tests/functional/arch/zxnext/add32a.asm @@ -0,0 +1,49 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add32a.bas b/tests/functional/arch/zxnext/add32a.bas new file mode 100644 index 000000000..94452fb0c --- /dev/null +++ b/tests/functional/arch/zxnext/add32a.bas @@ -0,0 +1,4 @@ + +REM another ADD32 test +DIM a As ULong +a = a + a + a diff --git a/tests/functional/arch/zxnext/add32b.asm b/tests/functional/arch/zxnext/add32b.asm new file mode 100644 index 000000000..fd5a2a8a5 --- /dev/null +++ b/tests/functional/arch/zxnext/add32b.asm @@ -0,0 +1,64 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + push hl + push de + ld de, 0 + ld hl, 2 + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + pop bc + add hl, bc + ex de, hl + pop bc + adc hl, bc + ex de, hl + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add32b.bas b/tests/functional/arch/zxnext/add32b.bas new file mode 100644 index 000000000..32ed2f068 --- /dev/null +++ b/tests/functional/arch/zxnext/add32b.bas @@ -0,0 +1,3 @@ +REM another ADD32 test +DIM a As Long +a = a + a + (a + 2 + a) diff --git a/tests/functional/arch/zxnext/add8.asm b/tests/functional/arch/zxnext/add8.asm new file mode 100644 index 000000000..01ed8bb36 --- /dev/null +++ b/tests/functional/arch/zxnext/add8.asm @@ -0,0 +1,49 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld (_b), a + ld a, (_a) + inc a + ld (_b), a + ld a, (_a) + ld (_b), a + ld a, (_a) + inc a + ld (_b), a + ld hl, (_a - 1) + ld a, (_a) + add a, h + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add8.bas b/tests/functional/arch/zxnext/add8.bas new file mode 100644 index 000000000..66403c245 --- /dev/null +++ b/tests/functional/arch/zxnext/add8.bas @@ -0,0 +1,10 @@ +' TEST for 8 bits ADD (both signed and unsigned) + +DIM a as Ubyte +DIM b as Ubyte + +b = a + 0 +b = a + 1 +b = 0 + a +b = 1 + a +b = a + a diff --git a/tests/functional/arch/zxnext/add8a.asm b/tests/functional/arch/zxnext/add8a.asm new file mode 100644 index 000000000..1e9f3e2f5 --- /dev/null +++ b/tests/functional/arch/zxnext/add8a.asm @@ -0,0 +1,40 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a - 1) + ld a, (_a) + add a, h + ld h, a + ld a, (_a) + add a, h + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add8a.bas b/tests/functional/arch/zxnext/add8a.bas new file mode 100644 index 000000000..98d639524 --- /dev/null +++ b/tests/functional/arch/zxnext/add8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a + a + a diff --git a/tests/functional/arch/zxnext/add8b.asm b/tests/functional/arch/zxnext/add8b.asm new file mode 100644 index 000000000..fc96cb4e0 --- /dev/null +++ b/tests/functional/arch/zxnext/add8b.asm @@ -0,0 +1,46 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a - 1) + ld a, (_a) + add a, h + push af + ld a, (_a) + add a, 2 + ld h, a + ld a, (_a) + add a, h + ld h, a + pop af + add a, h + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/add8b.bas b/tests/functional/arch/zxnext/add8b.bas new file mode 100644 index 000000000..fef66607a --- /dev/null +++ b/tests/functional/arch/zxnext/add8b.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a + a + (a + 2 + a) diff --git a/tests/functional/arch/zxnext/addf.asm b/tests/functional/arch/zxnext/addf.asm new file mode 100644 index 000000000..8e26e4d59 --- /dev/null +++ b/tests/functional/arch/zxnext/addf.asm @@ -0,0 +1,174 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + ld hl, _a + 4 + call .core.__FP_PUSH_REV + call .core.__ADDF + push bc + push de + push af + ld hl, _a + 4 + call .core.__FP_PUSH_REV + ld a, 082h + ld de, 00000h + ld bc, 00000h + call .core.__ADDF + push bc + push de + push af + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + call .core.__ADDF + call .core.__ADDF + ld hl, _a + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/addf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order AF DE BC (F not used). + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__ADDF: ; Addition + call __FPSTACK_PUSH2 + ; ------------- ROM ADD + rst 28h + defb 0fh ; ADD + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 38 "arch/zxnext/addf.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" + ; Routine to push Float pointed by HL + ; Into the stack. Notice that the hl points to the last + ; byte of the FP number. + ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers + push namespace core +__FP_PUSH_REV: + push hl + exx + pop hl + pop bc ; Return Address + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + push de + push bc ; Return Address + exx + ret + pop namespace +#line 39 "arch/zxnext/addf.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 40 "arch/zxnext/addf.bas" + END diff --git a/tests/functional/arch/zxnext/addf.bas b/tests/functional/arch/zxnext/addf.bas new file mode 100644 index 000000000..782365537 --- /dev/null +++ b/tests/functional/arch/zxnext/addf.bas @@ -0,0 +1,3 @@ +REM another Float test +DIM a As Float +a = a + a + (a + 2 + a) diff --git a/tests/functional/arch/zxnext/addf16.asm b/tests/functional/arch/zxnext/addf16.asm new file mode 100644 index 000000000..30849cbae --- /dev/null +++ b/tests/functional/arch/zxnext/addf16.asm @@ -0,0 +1,73 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +_b: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld de, 1 + ld hl, 0 + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld de, 1 + ld hl, 0 + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_b), hl + ld (_b + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/addf16.bas b/tests/functional/arch/zxnext/addf16.bas new file mode 100644 index 000000000..8e068aab3 --- /dev/null +++ b/tests/functional/arch/zxnext/addf16.bas @@ -0,0 +1,10 @@ +' TEST for ADD F16 + +DIM a as Fixed +DIM b as Fixed + +b = a + 0 +b = a + 1 +b = 0 + a +b = 1 + a +b = a + a diff --git a/tests/functional/arch/zxnext/addf16a.asm b/tests/functional/arch/zxnext/addf16a.asm new file mode 100644 index 000000000..d35f33022 --- /dev/null +++ b/tests/functional/arch/zxnext/addf16a.asm @@ -0,0 +1,49 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/addf16a.bas b/tests/functional/arch/zxnext/addf16a.bas new file mode 100644 index 000000000..a97333ffe --- /dev/null +++ b/tests/functional/arch/zxnext/addf16a.bas @@ -0,0 +1,4 @@ + +REM another ADDF16 tests +DIM a As Fixed +a = a + a + a diff --git a/tests/functional/arch/zxnext/addf16b.asm b/tests/functional/arch/zxnext/addf16b.asm new file mode 100644 index 000000000..6f4cf1ba1 --- /dev/null +++ b/tests/functional/arch/zxnext/addf16b.asm @@ -0,0 +1,64 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + push hl + push de + ld de, 2 + ld hl, 0 + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + ld bc, (_a) + add hl, bc + ex de, hl + ld bc, (_a + 2) + adc hl, bc + ex de, hl + pop bc + add hl, bc + ex de, hl + pop bc + adc hl, bc + ex de, hl + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/addf16b.bas b/tests/functional/arch/zxnext/addf16b.bas new file mode 100644 index 000000000..762129a31 --- /dev/null +++ b/tests/functional/arch/zxnext/addf16b.bas @@ -0,0 +1,4 @@ + +REM another ADDF16 test +DIM a As Fixed +a = a + a + (a + 2 + a) diff --git a/tests/functional/arch/zxnext/addstr.asm b/tests/functional/arch/zxnext/addstr.asm new file mode 100644 index 000000000..55be9b303 --- /dev/null +++ b/tests/functional/arch/zxnext/addstr.asm @@ -0,0 +1,682 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, .LABEL.__LABEL0 + ld hl, (_a) + call .core.__ADDSTR + ex de, hl + ld hl, _b + call .core.__STORE_STR2 + ld de, .LABEL.__LABEL1 + ld hl, (_a) + call .core.__ADDSTR + ex de, hl + ld hl, _b + call .core.__STORE_STR2 + ld de, (_a) + ld hl, .LABEL.__LABEL0 + call .core.__ADDSTR + ex de, hl + ld hl, _b + call .core.__STORE_STR2 + ld de, (_a) + ld hl, .LABEL.__LABEL1 + call .core.__ADDSTR + ex de, hl + ld hl, _b + call .core.__STORE_STR2 + ld de, (_a) + ld hl, (_a) + call .core.__ADDSTR + ex de, hl + ld hl, _b + call .core.__STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +.LABEL.__LABEL0: + DEFW 0000h +.LABEL.__LABEL1: + DEFW 0001h + DEFB 31h + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storestr2.asm" + ; Similar to __STORE_STR, but this one is called when + ; the value of B$ if already duplicated onto the stack. + ; So we needn't call STRASSING to create a duplication + ; HL = address of string memory variable + ; DE = address of 2n string. It just copies DE into (HL) + ; freeing (HL) previously. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/storestr2.asm" + push namespace core +__PISTORE_STR2: ; Indirect store temporary string at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR2: + ld c, (hl) ; Dereferences HL + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string variable address) +__STORE_STR2: + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = *HL (real string address) + push de + call __MEM_FREE + pop de + pop hl + ld (hl), e + inc hl + ld (hl), d + dec hl ; HL points to mem address variable. This might be useful in the future. + ret + pop namespace +#line 48 "arch/zxnext/addstr.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcat.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/strcat.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strlen.asm" + ; Returns len if a string + ; If a string is NULL, its len is also 0 + ; Result returned in HL + push namespace core +__STRLEN: ; Direct FASTCALL entry + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) ; LEN(str) in HL + ld l, a + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/strcat.asm" + push namespace core +__ADDSTR: ; Implements c$ = a$ + b$ + ; hl = &a$, de = &b$ (pointers) +__STRCAT2: ; This routine creates a new string in dynamic space + ; making room for it. Then copies a$ + b$ into it. + ; HL = a$, DE = b$ + PROC + LOCAL __STR_CONT + LOCAL __STRCATEND + push hl + call __STRLEN + ld c, l + ld b, h ; BC = LEN(a$) + ex (sp), hl ; (SP) = LEN (a$), HL = a$ + push hl ; Saves pointer to a$ + inc bc + inc bc ; +2 bytes to store length + ex de, hl + push hl + call __STRLEN + ; HL = len(b$) + add hl, bc ; Total str length => 2 + len(a$) + len(b$) + ld c, l + ld b, h ; BC = Total str length + 2 + call __MEM_ALLOC + pop de ; HL = c$, DE = b$ + ex de, hl ; HL = b$, DE = c$ + ex (sp), hl ; HL = a$, (SP) = b$ + exx + pop de ; D'E' = b$ + exx + pop bc ; LEN(a$) + ld a, d + or e + ret z ; If no memory: RETURN +__STR_CONT: + push de ; Address of c$ + ld a, h + or l + jr nz, __STR_CONT1 ; If len(a$) != 0 do copy + ; a$ is NULL => uses HL = DE for transfer + ld h, d + ld l, e + ld (hl), a ; This will copy 00 00 at (DE) location + inc de ; + dec bc ; Ensure BC will be set to 1 in the next step +__STR_CONT1: ; Copies a$ (HL) into c$ (DE) + inc bc + inc bc ; BC = BC + 2 + ldir ; MEMCOPY: c$ = a$ + pop hl ; HL = c$ + exx + push de ; Recovers b$; A ex hl,hl' would be very handy + exx + pop de ; DE = b$ +__STRCAT: ; ConCATenate two strings a$ = a$ + b$. HL = ptr to a$, DE = ptr to b$ + ; NOTE: Both DE, BC and AF are modified and lost + ; Returns HL (pointer to a$) + ; a$ Must be NOT NULL + ld a, d + or e + ret z ; Returns if de is NULL (nothing to copy) + push hl ; Saves HL to return it later + ld c, (hl) + inc hl + ld b, (hl) + inc hl + add hl, bc ; HL = end of (a$) string ; bc = len(a$) + push bc ; Saves LEN(a$) for later + ex de, hl ; DE = end of string (Begin of copy addr) + ld c, (hl) + inc hl + ld b, (hl) ; BC = len(b$) + ld a, b + or c + jr z, __STRCATEND; Return if len(b$) == 0 + push bc ; Save LEN(b$) + inc hl ; Skip 2nd byte of len(b$) + ldir ; Concatenate b$ + pop bc ; Recovers length (b$) + pop hl ; Recovers length (a$) + add hl, bc ; HL = LEN(a$) + LEN(b$) = LEN(a$+b$) + ex de, hl ; DE = LEN(a$+b$) + pop hl + ld (hl), e ; Updates new LEN and return + inc hl + ld (hl), d + dec hl + ret +__STRCATEND: + pop hl ; Removes Len(a$) + pop hl ; Restores original HL, so HL = a$ + ret + ENDP + pop namespace +#line 49 "arch/zxnext/addstr.bas" + END diff --git a/tests/functional/arch/zxnext/addstr.bas b/tests/functional/arch/zxnext/addstr.bas new file mode 100644 index 000000000..84ad57241 --- /dev/null +++ b/tests/functional/arch/zxnext/addstr.bas @@ -0,0 +1,10 @@ +' TEST for str ADD + +DIM a as String +DIM b as String + +b = a + "" +b = a + "1" +b = "" + a +b = "1" + a +b = a + a diff --git a/tests/functional/arch/zxnext/array00.asm b/tests/functional/arch/zxnext/array00.asm new file mode 100644 index 000000000..702f47984 --- /dev/null +++ b/tests/functional/arch/zxnext/array00.asm @@ -0,0 +1,72 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_bufferx: + DEFW .LABEL.__LABEL0 +_bufferx.__DATA__.__PTR__: + DEFW _bufferx.__DATA__ + DEFW 0 + DEFW 0 +_bufferx.__DATA__: + DEFB 0Ah + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0001h + DEFW 0006h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/array00.bas b/tests/functional/arch/zxnext/array00.bas new file mode 100644 index 000000000..db2a4f65b --- /dev/null +++ b/tests/functional/arch/zxnext/array00.bas @@ -0,0 +1,6 @@ +' Array initialization +DIM bufferx (0 to 4,0 to 5) as ubyte => {{10,0,0,0,0,0},_ + {0,0,0,0,0,0},_ + {0,0,0,0,0,0},_ + {0,0,0,0,0,0},_ + {0,0,0,0,0,0}} diff --git a/tests/functional/arch/zxnext/array01.asm b/tests/functional/arch/zxnext/array01.asm new file mode 100644 index 000000000..d93e4cbc0 --- /dev/null +++ b/tests/functional/arch/zxnext/array01.asm @@ -0,0 +1,54 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 5 + ld (_a.__DATA__ + 1), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/array01.bas b/tests/functional/arch/zxnext/array01.bas new file mode 100644 index 000000000..9e77593ba --- /dev/null +++ b/tests/functional/arch/zxnext/array01.bas @@ -0,0 +1,5 @@ +REM array assignation for Bytes + +DIM a(10) as UByte + +LET a(1) = 5 diff --git a/tests/functional/arch/zxnext/array02.asm b/tests/functional/arch/zxnext/array02.asm new file mode 100644 index 000000000..08d6ba9b6 --- /dev/null +++ b/tests/functional/arch/zxnext/array02.asm @@ -0,0 +1,56 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld (_a.__DATA__ + 1), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/array02.bas b/tests/functional/arch/zxnext/array02.bas new file mode 100644 index 000000000..a86a99342 --- /dev/null +++ b/tests/functional/arch/zxnext/array02.bas @@ -0,0 +1,6 @@ +REM array assignation for Bytes + +DIM a(10) as UByte +DIM b as Ubyte + +LET a(1) = b diff --git a/tests/functional/arch/zxnext/array03.asm b/tests/functional/arch/zxnext/array03.asm new file mode 100644 index 000000000..df67f3867 --- /dev/null +++ b/tests/functional/arch/zxnext/array03.asm @@ -0,0 +1,221 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__ARRAY + ld a, (_b) + ld (hl), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +#line 24 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + ENDP + pop namespace +#line 21 "arch/zxnext/array03.bas" + END diff --git a/tests/functional/arch/zxnext/array03.bas b/tests/functional/arch/zxnext/array03.bas new file mode 100644 index 000000000..10fdfe0a4 --- /dev/null +++ b/tests/functional/arch/zxnext/array03.bas @@ -0,0 +1,6 @@ +REM array assignation for Bytes + +DIM a(10) as UByte +DIM b as Ubyte + +LET a(b) = b diff --git a/tests/functional/arch/zxnext/array04.asm b/tests/functional/arch/zxnext/array04.asm new file mode 100644 index 000000000..558e10093 --- /dev/null +++ b/tests/functional/arch/zxnext/array04.asm @@ -0,0 +1,65 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 5 + ld (_a.__DATA__ + 2), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/array04.bas b/tests/functional/arch/zxnext/array04.bas new file mode 100644 index 000000000..571f1b25c --- /dev/null +++ b/tests/functional/arch/zxnext/array04.bas @@ -0,0 +1,5 @@ +REM array assignation for Bytes + +DIM a(10) as UInteger + +LET a(1) = 5 diff --git a/tests/functional/arch/zxnext/array05.asm b/tests/functional/arch/zxnext/array05.asm new file mode 100644 index 000000000..3587628c6 --- /dev/null +++ b/tests/functional/arch/zxnext/array05.asm @@ -0,0 +1,67 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_b) + ld (_a.__DATA__ + 2), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/array05.bas b/tests/functional/arch/zxnext/array05.bas new file mode 100644 index 000000000..2b9d662e4 --- /dev/null +++ b/tests/functional/arch/zxnext/array05.bas @@ -0,0 +1,6 @@ +REM array assignation for Bytes + +DIM a(10) as UInteger +DIM b as UInteger + +LET a(1) = b diff --git a/tests/functional/arch/zxnext/array06.asm b/tests/functional/arch/zxnext/array06.asm new file mode 100644 index 000000000..a1033631d --- /dev/null +++ b/tests/functional/arch/zxnext/array06.asm @@ -0,0 +1,232 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_b) + push hl + ld hl, _a + call .core.__ARRAY + ld de, (_b) + ld (hl), e + inc hl + ld (hl), d + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +#line 24 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + ENDP + pop namespace +#line 21 "arch/zxnext/array06.bas" + END diff --git a/tests/functional/arch/zxnext/array06.bas b/tests/functional/arch/zxnext/array06.bas new file mode 100644 index 000000000..61bebc351 --- /dev/null +++ b/tests/functional/arch/zxnext/array06.bas @@ -0,0 +1,6 @@ +REM array assignation for Bytes + +DIM a(10) as UInteger +DIM b as UInteger + +LET a(b) = b diff --git a/tests/functional/arch/zxnext/array07.asm b/tests/functional/arch/zxnext/array07.asm new file mode 100644 index 000000000..5a5852b6c --- /dev/null +++ b/tests/functional/arch/zxnext/array07.asm @@ -0,0 +1,966 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret +_test: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, (_b) + push hl + ld hl, _a + call .core.__ARRAY + pop de + call .core.__STORE_STR +_test__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +#line 24 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + ENDP + pop namespace +#line 41 "arch/zxnext/array07.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 42 "arch/zxnext/array07.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- + push namespace core +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer + ld (hl), e + inc hl + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + pop namespace +#line 43 "arch/zxnext/array07.bas" + END diff --git a/tests/functional/arch/zxnext/array07.bas b/tests/functional/arch/zxnext/array07.bas new file mode 100644 index 000000000..fe859566f --- /dev/null +++ b/tests/functional/arch/zxnext/array07.bas @@ -0,0 +1,6 @@ +DIM a$(10) +DIM b as Uinteger + +sub test(byval s as string) + a$(b) = s +end sub diff --git a/tests/functional/arch/zxnext/array08.asm b/tests/functional/arch/zxnext/array08.asm new file mode 100644 index 000000000..94d890c13 --- /dev/null +++ b/tests/functional/arch/zxnext/array08.asm @@ -0,0 +1,946 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00, 00 +_s: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_b) + push hl + ld hl, _a + call .core.__ARRAY + ld de, (_s) + call .core.__STORE_STR + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +#line 24 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + ENDP + pop namespace +#line 19 "arch/zxnext/array08.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 72 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- + push namespace core +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer + ld (hl), e + inc hl + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + pop namespace +#line 20 "arch/zxnext/array08.bas" + END diff --git a/tests/functional/arch/zxnext/array08.bas b/tests/functional/arch/zxnext/array08.bas new file mode 100644 index 000000000..758ec007a --- /dev/null +++ b/tests/functional/arch/zxnext/array08.bas @@ -0,0 +1,5 @@ +DIM a$(10) +DIM b as Uinteger +DIM s as String + +a$(b) = s diff --git a/tests/functional/arch/zxnext/array09.asm b/tests/functional/arch/zxnext/array09.asm new file mode 100644 index 000000000..a94cefa08 --- /dev/null +++ b/tests/functional/arch/zxnext/array09.asm @@ -0,0 +1,946 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00, 00 +_c: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW 0 +_a.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_b) + push hl + ld hl, _a + call .core.__ARRAY + ld de, (_c) + call .core.__STORE_STR + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" +#line 24 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + push namespace core +__ARRAY_PTR: ;; computes an array offset from a pointer + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ;; HL <-- [HL] +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL TMP_ARR_PTR ; Ptr to Array DATA region. Stored temporarily + LOCAL LBOUND_PTR, UBOUND_PTR ; LBound and UBound PTR indexes + LOCAL RET_ADDR ; Contains the return address popped from the stack + LBOUND_PTR EQU 23698 ; Uses MEMBOT as a temporary variable + UBOUND_PTR EQU LBOUND_PTR + 2 ; Next 2 bytes for UBOUND PTR + RET_ADDR EQU UBOUND_PTR + 2 ; Next 2 bytes for RET_ADDR + TMP_ARR_PTR EQU RET_ADDR + 2 ; Next 2 bytes for TMP_ARR_PTR + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE <-- PTR to Dim sizes table + ld (TMP_ARR_PTR), hl ; HL = Array __DATA__.__PTR__ + inc hl + inc hl + ld c, (hl) + inc hl + ld b, (hl) ; BC <-- Array __LBOUND__ PTR + ld (LBOUND_PTR), bc ; Store it for later +#line 66 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl ; HL <-- PTR to Dim sizes table, DE <-- dummy + ex (sp), hl ; Return address in HL, PTR Dim sizes table onto Stack + ld (RET_ADDR), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer to Dim sizes table + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Element Offset "accumulator" +LOOP: + ex de, hl ; DE = Element Offset + ld hl, (LBOUND_PTR) + ld a, h + or l + ld b, h + ld c, l + jr z, 1f + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld (LBOUND_PTR), hl +1: + pop hl ; Get next index (Ai) from the stack + sbc hl, bc ; Subtract LBOUND +#line 116 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + add hl, de ; Adds current index + exx ; Checks if B'C' = 0 + ld a, b ; Which means we must exit (last element is not multiplied by anything) + or c + jr z, ARRAY_END ; if B'Ci == 0 we are done + dec bc ; Decrements loop counter + ld e, (hl) ; Loads next dimension size into D'E' + inc hl + ld d, (hl) + inc hl + push de + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + LOCAL ARRAY_SIZE_LOOP + ex de, hl + ld hl, 0 + ld b, a +ARRAY_SIZE_LOOP: + add hl, de + djnz ARRAY_SIZE_LOOP +#line 156 "/zxbasic/src/lib/arch/zxnext/runtime/array.asm" + ex de, hl + ld hl, (TMP_ARR_PTR) + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + add hl, de ; Adds element start + ld de, (RET_ADDR) + push de + ret + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret + ENDP + pop namespace +#line 19 "arch/zxnext/array09.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storestr.asm" +; vim:ts=4:et:sw=4 + ; Stores value of current string pointed by DE register into address pointed by HL + ; Returns DE = Address pointer (&a$) + ; Returns HL = HL (b$ => might be needed later to free it from the heap) + ; + ; e.g. => HL = _variableName (DIM _variableName$) + ; DE = Address into the HEAP + ; + ; This function will resize (REALLOC) the space pointed by HL + ; before copying the content of b$ into a$ +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the MIT license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the MIT license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be freed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 70 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; --------------------------------------------------------------------- + ; MEM_ALLOC + ; Allocates a block of memory in the heap. + ; + ; Parameters + ; BC = Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; --------------------------------------------------------------------- + push namespace core +MEM_ALLOC: +__MEM_ALLOC: ; Returns the 1st free block found of the given length (in BC) + PROC + LOCAL __MEM_LOOP + LOCAL __MEM_DONE + LOCAL __MEM_SUBTRACT + LOCAL __MEM_START + LOCAL TEMP, TEMP0 + TEMP EQU TEMP0 + 1 + ld hl, 0 + ld (TEMP), hl +__MEM_START: + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start + inc bc + inc bc ; BC = BC + 2 ; block size needs 2 extra bytes for hidden pointer +__MEM_LOOP: ; Loads lengh at (HL, HL+). If Lenght >= BC, jump to __MEM_DONE + ld a, h ; HL = NULL (No memory available?) + or l +#line 113 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zxnext/runtime/alloc.asm" + ; HL = Pointer to Free block + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = Block Length + push hl ; HL = *pointer to -> next block + ex de, hl + or a ; CF = 0 + sbc hl, bc ; FREE >= BC (Length) (HL = BlockLength - Length) + jp nc, __MEM_DONE + pop hl + ld (TEMP), hl + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl + jp __MEM_LOOP +__MEM_DONE: ; A free block has been found. + ; Check if at least 4 bytes remains free (HL >= 4) + push hl + exx ; exx to preserve bc + pop hl + ld bc, 4 + or a + sbc hl, bc + exx + jp nc, __MEM_SUBTRACT + ; At this point... + ; less than 4 bytes remains free. So we return this block entirely + ; We must link the previous block with the next to this one + ; (DE) => Pointer to next block + ; (TEMP) => &(previous->next) + pop hl ; Discard current block pointer + push de + ex de, hl ; DE = Previous block pointer; (HL) = Next block pointer + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex de, hl ; HL = Previous block pointer; DE = Next block pointer +TEMP0: + ld hl, 0 ; Pre-previous block pointer + ld (hl), e + inc hl + ld (hl), d ; LINKED + pop hl ; Returning block. + ret +__MEM_SUBTRACT: + ; At this point we have to store HL value (Length - BC) into (DE - 2) + ex de, hl + dec hl + ld (hl), d + dec hl + ld (hl), e ; Store new block length + add hl, de ; New length + DE => free-block start + pop de ; Remove previous HL off the stack + ld (hl), c ; Store length on its 1st word + inc hl + ld (hl), b + inc hl ; Return hl + ret + ENDP + pop namespace +#line 71 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 72 "/zxbasic/src/lib/arch/zxnext/runtime/realloc.asm" + ; --------------------------------------------------------------------- + ; MEM_REALLOC + ; Reallocates a block of memory in the heap. + ; + ; Parameters + ; HL = Pointer to the original block + ; BC = New Length of requested memory block + ; +; Returns: + ; HL = Pointer to the allocated block in memory. Returns 0 (NULL) + ; if the block could not be allocated (out of memory) + ; +; Notes: + ; If BC = 0, the block is freed, otherwise + ; the content of the original block is copied to the new one, and + ; the new size is adjusted. If BC < original length, the content + ; will be truncated. Otherwise, extra block content might contain + ; memory garbage. + ; + ; --------------------------------------------------------------------- + push namespace core +__REALLOC: ; Reallocates block pointed by HL, with new length BC + PROC + LOCAL __REALLOC_END + ld a, h + or l + jp z, __MEM_ALLOC ; If HL == NULL, just do a malloc + ld e, (hl) + inc hl + ld d, (hl) ; DE = First 2 bytes of HL block + push hl + exx + pop de + inc de ; DE' <- HL + 2 + exx ; DE' <- HL (Saves current pointer into DE') + dec hl ; HL = Block start + push de + push bc + call __MEM_FREE ; Frees current block + pop bc + push bc + call __MEM_ALLOC ; Gets a new block of length BC + pop bc + pop de + ld a, h + or l + ret z ; Return if HL == NULL (No memory) + ld (hl), e + inc hl + ld (hl), d + inc hl ; Recovers first 2 bytes in HL + dec bc + dec bc ; BC = BC - 2 (Two bytes copied) + ld a, b + or c + jp z, __REALLOC_END ; Ret if nothing to copy (BC == 0) + exx + push de + exx + pop de ; DE <- DE' ; Start of remaining block + push hl ; Saves current Block + 2 start + ex de, hl ; Exchanges them: DE is destiny block + ldir ; Copies BC Bytes + pop hl ; Recovers Block + 2 start +__REALLOC_END: + dec hl ; Set HL + dec hl ; To begin of block + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/strcpy.asm" + ; String library + push namespace core +__STRASSIGN: ; Performs a$ = b$ (HL = address of a$; DE = Address of b$) + PROC + LOCAL __STRREALLOC + LOCAL __STRCONTINUE + LOCAL __B_IS_NULL + LOCAL __NOTHING_TO_COPY + ld b, d + ld c, e + ld a, b + or c + jr z, __B_IS_NULL + ex de, hl + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(b$) + ex de, hl ; DE = &b$ +__B_IS_NULL: ; Jumps here if B$ pointer is NULL + inc bc + inc bc ; BC = BC + 2 ; (LEN(b$) + 2 bytes for storing length) + push de + push hl + ld a, h + or l + jr z, __STRREALLOC + dec hl + ld d, (hl) + dec hl + ld e, (hl) ; DE = MEMBLOCKSIZE(a$) + dec de + dec de ; DE = DE - 2 ; (Membloksize takes 2 bytes for memblock length) + ld h, b + ld l, c ; HL = LEN(b$) + 2 => Minimum block size required + ex de, hl ; Now HL = BLOCKSIZE(a$), DE = LEN(b$) + 2 + or a ; Prepare to subtract BLOCKSIZE(a$) - LEN(b$) + sbc hl, de ; Carry if len(b$) > Blocklen(a$) + jr c, __STRREALLOC ; No need to realloc + ; Need to reallocate at least to len(b$) + 2 + ex de, hl ; DE = Remaining bytes in a$ mem block. + ld hl, 4 + sbc hl, de ; if remaining bytes < 4 we can continue + jr nc,__STRCONTINUE ; Otherwise, we realloc, to free some bytes +__STRREALLOC: + pop hl + call __REALLOC ; Returns in HL a new pointer with BC bytes allocated + push hl +__STRCONTINUE: ; Pops hl and de SWAPPED + pop de ; DE = &a$ + pop hl ; HL = &b$ + ld a, d ; Return if not enough memory for new length + or e + ret z ; Return if DE == NULL (0) +__STRCPY: ; Copies string pointed by HL into string pointed by DE + ; Returns DE as HL (new pointer) + ld a, h + or l + jr z, __NOTHING_TO_COPY + ld c, (hl) + inc hl + ld b, (hl) + dec hl + inc bc + inc bc + push de + ldir + pop hl + ret +__NOTHING_TO_COPY: + ex de, hl + ld (hl), e + inc hl + ld (hl), d + dec hl + ret + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignment at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR: ; Indirect assignment, hl point to a pointer to a pointer to the heap! + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) +__STORE_STR: + push de ; Pointer to b$ + push hl ; Pointer to a$ + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = (HL) + call __STRASSIGN ; HL (a$) = DE (b$); HL changed to a new dynamic memory allocation + ex de, hl ; DE = new address of a$ + pop hl ; Recover variable memory address pointer + ld (hl), e + inc hl + ld (hl), d ; Stores a$ ptr into element ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + pop namespace +#line 20 "arch/zxnext/array09.bas" + END diff --git a/tests/functional/arch/zxnext/array09.bas b/tests/functional/arch/zxnext/array09.bas new file mode 100644 index 000000000..0d189285f --- /dev/null +++ b/tests/functional/arch/zxnext/array09.bas @@ -0,0 +1,6 @@ +REM array assignation for Bytes + +DIM a(10) as String +DIM b as UInteger + +LET a(b) = c$ diff --git a/tests/functional/arch/zxnext/div32.asm b/tests/functional/arch/zxnext/div32.asm new file mode 100644 index 000000000..c0400c71e --- /dev/null +++ b/tests/functional/arch/zxnext/div32.asm @@ -0,0 +1,272 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + call .core.__NEGF + push bc + push de + ld h, a + pop de + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/negf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/u32tofreg.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/u32tofreg.asm" + push namespace core +__I8TOFREG: + ld l, a + rlca + sbc a, a ; A = SGN(A) + ld h, a + ld e, a + ld d, a +__I32TOFREG: ; Converts a 32bit signed integer (stored in DEHL) + ; to a Floating Point Number returned in (A ED CB) + ld a, d + or a ; Test sign + jp p, __U32TOFREG ; It was positive, proceed as 32bit unsigned + call __NEG32 ; Convert it to positive + call __U32TOFREG ; Convert it to Floating point + set 7, e ; Put the sign bit (negative) in the 31bit of mantissa + ret +__U8TOFREG: + ; Converts an unsigned 8 bit (A) to Floating point + ld l, a + ld h, 0 + ld e, h + ld d, h +__U32TOFREG: ; Converts an unsigned 32 bit integer (DEHL) + ; to a Floating point number returned in A ED CB + PROC + LOCAL __U32TOFREG_END + ld a, d + or e + or h + or l + ld b, d + ld c, e ; Returns 00 0000 0000 if ZERO + ret z + push de + push hl + exx + pop de ; Loads integer into B'C' D'E' + pop bc + exx + ld l, 128 ; Exponent + ld bc, 0 ; DEBC = 0 + ld d, b + ld e, c +__U32TOFREG_LOOP: ; Also an entry point for __F16TOFREG + exx + ld a, d ; B'C'D'E' == 0 ? + or e + or b + or c + jp z, __U32TOFREG_END ; We are done + srl b ; Shift B'C' D'E' >> 1, output bit stays in Carry + rr c + rr d + rr e + exx + rr e ; Shift EDCB >> 1, inserting the carry on the left + rr d + rr c + rr b + inc l ; Increment exponent + jp __U32TOFREG_LOOP +__U32TOFREG_END: + exx + ld a, l ; Puts the exponent in a + res 7, e ; Sets the sign bit to 0 (positive) + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/negf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ftou32reg.asm" + push namespace core +__FTOU32REG: ; Converts a Float to (un)signed 32 bit integer (NOTE: It's ALWAYS 32 bit signed) + ; Input FP number in A EDCB (A exponent, EDCB mantissa) + ; Output: DEHL 32 bit number (signed) + PROC + LOCAL __IS_FLOAT + LOCAL __NEGATE + or a + jr nz, __IS_FLOAT + ; Here if it is a ZX ROM Integer + ld h, c + ld l, d + ld d, e + ret +__IS_FLOAT: ; Jumps here if it is a true floating point number + ld h, e + push hl ; Stores it for later (Contains Sign in H) + push de + push bc + exx + pop de ; Loads mantissa into C'B' E'D' + pop bc ; + set 7, c ; Highest mantissa bit is always 1 + exx + ld hl, 0 ; DEHL = 0 + ld d, h + ld e, l + ;ld a, c ; Get exponent + sub 128 ; Exponent -= 128 + jr z, __FTOU32REG_END ; If it was <= 128, we are done (Integers must be > 128) + jr c, __FTOU32REG_END ; It was decimal (0.xxx). We are done (return 0) + ld b, a ; Loop counter = exponent - 128 +__FTOU32REG_LOOP: + exx ; Shift C'B' E'D' << 1, output bit stays in Carry + sla d + rl e + rl b + rl c + exx ; Shift DEHL << 1, inserting the carry on the right + rl l + rl h + rl e + rl d + djnz __FTOU32REG_LOOP +__FTOU32REG_END: + pop af ; Take the sign bit + or a ; Sets SGN bit to 1 if negative + jp m, __NEGATE ; Negates DEHL + ret +__NEGATE: + exx + ld a, d + or e + or b + or c + exx + jr z, __END + inc l + jr nz, __END + inc h + jr nz, __END + inc de + LOCAL __END +__END: + jp __NEG32 + ENDP +__FTOU8: ; Converts float in C ED LH to Unsigned byte in A + call __FTOU32REG + ld a, l + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/negf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/negf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses C EDHL registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order BC DE HL (B not used). + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__NEGF: ; A = -A + call __FPSTACK_PUSH + ; ------------- ROM NEGATE + rst 28h + defb 1Bh ; NEGF + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 23 "arch/zxnext/div32.bas" + END diff --git a/tests/functional/arch/zxnext/div32.bas b/tests/functional/arch/zxnext/div32.bas new file mode 100644 index 000000000..ead0b2565 --- /dev/null +++ b/tests/functional/arch/zxnext/div32.bas @@ -0,0 +1,3 @@ +DIM a as Fixed + +LET a = a / -1 diff --git a/tests/functional/arch/zxnext/divf00.asm b/tests/functional/arch/zxnext/divf00.asm new file mode 100644 index 000000000..fc9ddbacf --- /dev/null +++ b/tests/functional/arch/zxnext/divf00.asm @@ -0,0 +1,231 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 80h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_b: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _b + 4 + call .core.__FP_PUSH_REV + ld a, 082h + ld de, 00000h + ld bc, 00000h + call .core.__DIVF + ld hl, _b + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses C EDHL registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order BC DE HL (B not used). + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__DIVF: ; Division + PROC + LOCAL __DIVBYZERO + LOCAL TMP, ERR_SP + TMP EQU 23629 ;(DEST) + ERR_SP EQU 23613 + call __FPSTACK_PUSH2 + ld hl, (ERR_SP) + ld (TMP), hl + ld hl, __DIVBYZERO + push hl + ld hl, 0 + add hl, sp + ld (ERR_SP), hl + ; ------------- ROM DIV + rst 28h + defb 01h ; EXCHANGE + defb 05h ; DIV + defb 38h; ; END CALC + pop hl + ld hl, (TMP) + ld (ERR_SP), hl + jp __FPSTACK_POP +__DIVBYZERO: + ld hl, (TMP) + ld (ERR_SP), hl + ld a, ERROR_NumberTooBig + ld (ERR_NR), a + ; Returns 0 on DIV BY ZERO error + xor a + ld b, a + ld c, a + ld d, a + ld e, a + ret + ENDP + pop namespace +#line 21 "arch/zxnext/divf00.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" + ; Routine to push Float pointed by HL + ; Into the stack. Notice that the hl points to the last + ; byte of the FP number. + ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers + push namespace core +__FP_PUSH_REV: + push hl + exx + pop hl + pop bc ; Return Address + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + push de + push bc ; Return Address + exx + ret + pop namespace +#line 22 "arch/zxnext/divf00.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 23 "arch/zxnext/divf00.bas" + END diff --git a/tests/functional/arch/zxnext/divf00.bas b/tests/functional/arch/zxnext/divf00.bas new file mode 100644 index 000000000..9b6450fd8 --- /dev/null +++ b/tests/functional/arch/zxnext/divf00.bas @@ -0,0 +1,4 @@ +DIM a as FLOAT = 0.5 +DIM b as FLOAT = 1 + +b = b / 2 diff --git a/tests/functional/arch/zxnext/divf01.asm b/tests/functional/arch/zxnext/divf01.asm new file mode 100644 index 000000000..e0b417700 --- /dev/null +++ b/tests/functional/arch/zxnext/divf01.asm @@ -0,0 +1,207 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 80h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_b: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld de, (_b + 1) + ld bc, (_b + 3) + ld hl, 00000h + push hl + ld hl, 00000h + push hl + ld h, 082h + push hl + call .core.__DIVF + ld hl, _b + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses C EDHL registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order BC DE HL (B not used). + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__DIVF: ; Division + PROC + LOCAL __DIVBYZERO + LOCAL TMP, ERR_SP + TMP EQU 23629 ;(DEST) + ERR_SP EQU 23613 + call __FPSTACK_PUSH2 + ld hl, (ERR_SP) + ld (TMP), hl + ld hl, __DIVBYZERO + push hl + ld hl, 0 + add hl, sp + ld (ERR_SP), hl + ; ------------- ROM DIV + rst 28h + defb 01h ; EXCHANGE + defb 05h ; DIV + defb 38h; ; END CALC + pop hl + ld hl, (TMP) + ld (ERR_SP), hl + jp __FPSTACK_POP +__DIVBYZERO: + ld hl, (TMP) + ld (ERR_SP), hl + ld a, ERROR_NumberTooBig + ld (ERR_NR), a + ; Returns 0 on DIV BY ZERO error + xor a + ld b, a + ld c, a + ld d, a + ld e, a + ret + ENDP + pop namespace +#line 25 "arch/zxnext/divf01.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 26 "arch/zxnext/divf01.bas" + END diff --git a/tests/functional/arch/zxnext/divf01.bas b/tests/functional/arch/zxnext/divf01.bas new file mode 100644 index 000000000..fb917f97d --- /dev/null +++ b/tests/functional/arch/zxnext/divf01.bas @@ -0,0 +1,4 @@ +DIM a as FLOAT = 0.5 +DIM b as FLOAT = 1 + +b = 2 / b diff --git a/tests/functional/arch/zxnext/divf16.asm b/tests/functional/arch/zxnext/divf16.asm new file mode 100644 index 000000000..7340ea501 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16.asm @@ -0,0 +1,313 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +_b: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld de, 2 + ld hl, 0 + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld de, 4 + ld hl, 0 + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld bc, 0 + push bc + ld bc, 0 + push bc + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld bc, 1 + push bc + ld bc, 0 + push bc + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld bc, 2 + push bc + ld bc, 0 + push bc + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld bc, 4 + push bc + ld bc, 0 + push bc + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__DIVF16 + ld (_b), hl + ld (_b + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + push namespace core +__DIVF16: ; 16.16 Fixed point Division (signed) + ; DE.HL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + ex de, hl ; D'E'.H'L' Dividend +__DIVF16START: ; FAST Entry: DEHL => Dividend, D'E'H'L' => Divisor + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with D'E'.H'L' + ex af, af' + xor d + ex af, af' ; Stores sign of the result for later + bit 7, d ; Negative? + call nz, __NEG32 + exx ; Now we have DE.HL => Dividend + ld b, 16 +__SHIFTALOOP: ; Tries to shift Dividend to the left + bit 7, d + jp nz, __SHIFTB + add hl, hl + ex de, hl + adc hl, hl + ex de, hl + djnz __SHIFTALOOP + jp __DOF16_DIVRDY +__SHIFTB: ; Cannot shift Dividend more to the left, try to shift Divisor to the right + ld a, b + exx + ld b, a + ; Divisor is in DEHL +__SHIFTBLOOP: + bit 1, l + jp nz, __DOF16_DIVIDE + sra d + rr e + rr h + rr l + djnz __SHIFTBLOOP +__DOF16_DIVIDE: + ld a, b + exx + ld b, a +__DOF16_DIVRDY: + exx + ex de, hl + push bc + call __DIVU32START + pop bc + xor a + or b + jp z, __ENDF16DIV +__SHIFTCLOOP: + add hl, hl ; Shift DECIMAL PART << 1 + ex de, hl + adc hl, hl ; Shift INTEGER PART << 1 Plus Carry + ex de, hl + djnz __SHIFTCLOOP +__ENDF16DIV: ; Put the sign on the result + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there + pop namespace +#line 80 "arch/zxnext/divf16.bas" + END diff --git a/tests/functional/arch/zxnext/divf16.bas b/tests/functional/arch/zxnext/divf16.bas new file mode 100644 index 000000000..3fa4dba4a --- /dev/null +++ b/tests/functional/arch/zxnext/divf16.bas @@ -0,0 +1,13 @@ +' TEST for Fixed Division + +DIM a as Fixed +DIM b as Fixed + +b = a / 1 +b = a / 2 +b = a / 4 +b = 0 / a +b = 1 / a +b = 2 / a +b = 4 / a +b = a / a diff --git a/tests/functional/arch/zxnext/divf16a.asm b/tests/functional/arch/zxnext/divf16a.asm new file mode 100644 index 000000000..a4d8adf73 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16a.asm @@ -0,0 +1,258 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__DIVF16 + push de + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__DIVF16 + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + push namespace core +__DIVF16: ; 16.16 Fixed point Division (signed) + ; DE.HL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + ex de, hl ; D'E'.H'L' Dividend +__DIVF16START: ; FAST Entry: DEHL => Dividend, D'E'H'L' => Divisor + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with D'E'.H'L' + ex af, af' + xor d + ex af, af' ; Stores sign of the result for later + bit 7, d ; Negative? + call nz, __NEG32 + exx ; Now we have DE.HL => Dividend + ld b, 16 +__SHIFTALOOP: ; Tries to shift Dividend to the left + bit 7, d + jp nz, __SHIFTB + add hl, hl + ex de, hl + adc hl, hl + ex de, hl + djnz __SHIFTALOOP + jp __DOF16_DIVRDY +__SHIFTB: ; Cannot shift Dividend more to the left, try to shift Divisor to the right + ld a, b + exx + ld b, a + ; Divisor is in DEHL +__SHIFTBLOOP: + bit 1, l + jp nz, __DOF16_DIVIDE + sra d + rr e + rr h + rr l + djnz __SHIFTBLOOP +__DOF16_DIVIDE: + ld a, b + exx + ld b, a +__DOF16_DIVRDY: + exx + ex de, hl + push bc + call __DIVU32START + pop bc + xor a + or b + jp z, __ENDF16DIV +__SHIFTCLOOP: + add hl, hl ; Shift DECIMAL PART << 1 + ex de, hl + adc hl, hl ; Shift INTEGER PART << 1 Plus Carry + ex de, hl + djnz __SHIFTCLOOP +__ENDF16DIV: ; Put the sign on the result + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there + pop namespace +#line 27 "arch/zxnext/divf16a.bas" + END diff --git a/tests/functional/arch/zxnext/divf16a.bas b/tests/functional/arch/zxnext/divf16a.bas new file mode 100644 index 000000000..5ca0bd882 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16a.bas @@ -0,0 +1,4 @@ + +REM another DIV Fixed tests +DIM a As Fixed +a = a / a / a diff --git a/tests/functional/arch/zxnext/divf16b.asm b/tests/functional/arch/zxnext/divf16b.asm new file mode 100644 index 000000000..0e63242a9 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16b.asm @@ -0,0 +1,268 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__DIVF16 + push de + push hl + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld de, 2 + ld hl, 0 + call .core.__DIVF16 + push de + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__DIVF16 + call .core.__DIVF16 + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + push namespace core +__DIVF16: ; 16.16 Fixed point Division (signed) + ; DE.HL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + ex de, hl ; D'E'.H'L' Dividend +__DIVF16START: ; FAST Entry: DEHL => Dividend, D'E'H'L' => Divisor + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with D'E'.H'L' + ex af, af' + xor d + ex af, af' ; Stores sign of the result for later + bit 7, d ; Negative? + call nz, __NEG32 + exx ; Now we have DE.HL => Dividend + ld b, 16 +__SHIFTALOOP: ; Tries to shift Dividend to the left + bit 7, d + jp nz, __SHIFTB + add hl, hl + ex de, hl + adc hl, hl + ex de, hl + djnz __SHIFTALOOP + jp __DOF16_DIVRDY +__SHIFTB: ; Cannot shift Dividend more to the left, try to shift Divisor to the right + ld a, b + exx + ld b, a + ; Divisor is in DEHL +__SHIFTBLOOP: + bit 1, l + jp nz, __DOF16_DIVIDE + sra d + rr e + rr h + rr l + djnz __SHIFTBLOOP +__DOF16_DIVIDE: + ld a, b + exx + ld b, a +__DOF16_DIVRDY: + exx + ex de, hl + push bc + call __DIVU32START + pop bc + xor a + or b + jp z, __ENDF16DIV +__SHIFTCLOOP: + add hl, hl ; Shift DECIMAL PART << 1 + ex de, hl + adc hl, hl ; Shift INTEGER PART << 1 Plus Carry + ex de, hl + djnz __SHIFTCLOOP +__ENDF16DIV: ; Put the sign on the result + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there + pop namespace +#line 37 "arch/zxnext/divf16b.bas" + END diff --git a/tests/functional/arch/zxnext/divf16b.bas b/tests/functional/arch/zxnext/divf16b.bas new file mode 100644 index 000000000..601774854 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16b.bas @@ -0,0 +1,4 @@ + +REM another ADD Fixed tests +DIM a As Fixed +a = a / a / (a / 2 / a) diff --git a/tests/functional/arch/zxnext/divf16c.asm b/tests/functional/arch/zxnext/divf16c.asm new file mode 100644 index 000000000..043eb4860 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16c.asm @@ -0,0 +1,318 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 02h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__SWAP32 + call .core.__DIVF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__DIVF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__DIVF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__DIVF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 2 + push bc + ld bc, 0 + push bc + call .core.__DIVF16 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + push namespace core +__DIVF16: ; 16.16 Fixed point Division (signed) + ; DE.HL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + ex de, hl ; D'E'.H'L' Dividend +__DIVF16START: ; FAST Entry: DEHL => Dividend, D'E'H'L' => Divisor + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with D'E'.H'L' + ex af, af' + xor d + ex af, af' ; Stores sign of the result for later + bit 7, d ; Negative? + call nz, __NEG32 + exx ; Now we have DE.HL => Dividend + ld b, 16 +__SHIFTALOOP: ; Tries to shift Dividend to the left + bit 7, d + jp nz, __SHIFTB + add hl, hl + ex de, hl + adc hl, hl + ex de, hl + djnz __SHIFTALOOP + jp __DOF16_DIVRDY +__SHIFTB: ; Cannot shift Dividend more to the left, try to shift Divisor to the right + ld a, b + exx + ld b, a + ; Divisor is in DEHL +__SHIFTBLOOP: + bit 1, l + jp nz, __DOF16_DIVIDE + sra d + rr e + rr h + rr l + djnz __SHIFTBLOOP +__DOF16_DIVIDE: + ld a, b + exx + ld b, a +__DOF16_DIVRDY: + exx + ex de, hl + push bc + call __DIVU32START + pop bc + xor a + or b + jp z, __ENDF16DIV +__SHIFTCLOOP: + add hl, hl ; Shift DECIMAL PART << 1 + ex de, hl + adc hl, hl ; Shift INTEGER PART << 1 Plus Carry + ex de, hl + djnz __SHIFTCLOOP +__ENDF16DIV: ; Put the sign on the result + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there + pop namespace +#line 59 "arch/zxnext/divf16c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 60 "arch/zxnext/divf16c.bas" + END diff --git a/tests/functional/arch/zxnext/divf16c.bas b/tests/functional/arch/zxnext/divf16c.bas new file mode 100644 index 000000000..642235083 --- /dev/null +++ b/tests/functional/arch/zxnext/divf16c.bas @@ -0,0 +1,9 @@ +dim level as fixed = 1 +dim le as fixed = 2 +dim l as fixed + +l = le / (level + 0) +l = le / level +l = (le + 0) / level +l = (le + 0) / (level + 0) +l = 2 / (level + 0) diff --git a/tests/functional/arch/zxnext/divi16a.asm b/tests/functional/arch/zxnext/divi16a.asm new file mode 100644 index 000000000..bd84b4ef3 --- /dev/null +++ b/tests/functional/arch/zxnext/divi16a.asm @@ -0,0 +1,132 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a) + call .core.__DIVU16 + ld de, (_a) + call .core.__DIVU16 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 19 "arch/zxnext/divi16a.bas" + END diff --git a/tests/functional/arch/zxnext/divi16a.bas b/tests/functional/arch/zxnext/divi16a.bas new file mode 100644 index 000000000..4a4d179d5 --- /dev/null +++ b/tests/functional/arch/zxnext/divi16a.bas @@ -0,0 +1,4 @@ + +REM another DIV16 test +DIM a As UInteger +a = a / a / a diff --git a/tests/functional/arch/zxnext/divi16b.asm b/tests/functional/arch/zxnext/divi16b.asm new file mode 100644 index 000000000..5ed15c322 --- /dev/null +++ b/tests/functional/arch/zxnext/divi16b.asm @@ -0,0 +1,139 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a) + call .core.__DIVU16 + push hl + ld hl, (_a) + srl h + rr l + ld de, (_a) + call .core.__DIVU16 + ex de, hl + pop hl + call .core.__DIVU16 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 26 "arch/zxnext/divi16b.bas" + END diff --git a/tests/functional/arch/zxnext/divi16b.bas b/tests/functional/arch/zxnext/divi16b.bas new file mode 100644 index 000000000..34d78b02f --- /dev/null +++ b/tests/functional/arch/zxnext/divi16b.bas @@ -0,0 +1,4 @@ + +REM another DIV16 test +DIM a As UInteger +a = a / a / (a / 2 / a) diff --git a/tests/functional/arch/zxnext/divi32c.asm b/tests/functional/arch/zxnext/divi32c.asm new file mode 100644 index 000000000..e9360bd2a --- /dev/null +++ b/tests/functional/arch/zxnext/divi32c.asm @@ -0,0 +1,250 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 02h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__DIVI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__DIVI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__DIVI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__DIVI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 2 + push bc + call .core.__SWAP32 + call .core.__DIVI32 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 62 "arch/zxnext/divi32c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 63 "arch/zxnext/divi32c.bas" + END diff --git a/tests/functional/arch/zxnext/divi32c.bas b/tests/functional/arch/zxnext/divi32c.bas new file mode 100644 index 000000000..714b198d5 --- /dev/null +++ b/tests/functional/arch/zxnext/divi32c.bas @@ -0,0 +1,9 @@ +dim level as long = 1 +dim le as long = 2 +dim l as long + +l = le / (level + 0) +l = le / level +l = (le + 0) / level +l = (le + 0) / (level + 0) +l = 2 / (level + 0) diff --git a/tests/functional/arch/zxnext/divi8.asm b/tests/functional/arch/zxnext/divi8.asm new file mode 100644 index 000000000..f8a0483e3 --- /dev/null +++ b/tests/functional/arch/zxnext/divi8.asm @@ -0,0 +1,128 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld (_b), a + ld a, (_a) + sra a + ld (_b), a + ld a, (_a) + ld h, 4 + call .core.__DIVI8_FAST + ld (_b), a + xor a + ld (_b), a + ld a, 1 + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld (_b), a + ld a, 2 + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld (_b), a + ld a, 4 + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld (_b), a + ld a, (_a) + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 40 "arch/zxnext/divi8.bas" + END diff --git a/tests/functional/arch/zxnext/divi8.bas b/tests/functional/arch/zxnext/divi8.bas new file mode 100644 index 000000000..6d0fa4935 --- /dev/null +++ b/tests/functional/arch/zxnext/divi8.bas @@ -0,0 +1,13 @@ +' TEST for Booleand AND 8 bits + +DIM a as Byte +DIM b as Byte + +b = a / 1 +b = a / 2 +b = a / 4 +b = 0 / a +b = 1 / a +b = 2 / a +b = 4 / a +b = a / a diff --git a/tests/functional/arch/zxnext/divi8a.asm b/tests/functional/arch/zxnext/divi8a.asm new file mode 100644 index 000000000..77a1ebba2 --- /dev/null +++ b/tests/functional/arch/zxnext/divi8a.asm @@ -0,0 +1,105 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 19 "arch/zxnext/divi8a.bas" + END diff --git a/tests/functional/arch/zxnext/divi8a.bas b/tests/functional/arch/zxnext/divi8a.bas new file mode 100644 index 000000000..0e2d69aa5 --- /dev/null +++ b/tests/functional/arch/zxnext/divi8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a / a / a diff --git a/tests/functional/arch/zxnext/divi8b.asm b/tests/functional/arch/zxnext/divi8b.asm new file mode 100644 index 000000000..86c255297 --- /dev/null +++ b/tests/functional/arch/zxnext/divi8b.asm @@ -0,0 +1,111 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__DIVI8_FAST + push af + ld a, (_a) + sra a + ld hl, (_a - 1) + call .core.__DIVI8_FAST + ld h, a + pop af + call .core.__DIVI8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 25 "arch/zxnext/divi8b.bas" + END diff --git a/tests/functional/arch/zxnext/divi8b.bas b/tests/functional/arch/zxnext/divi8b.bas new file mode 100644 index 000000000..17ea37bf9 --- /dev/null +++ b/tests/functional/arch/zxnext/divi8b.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a / a / (a / 2 / a) diff --git a/tests/functional/arch/zxnext/divu16.asm b/tests/functional/arch/zxnext/divu16.asm new file mode 100644 index 000000000..fcd52c9fe --- /dev/null +++ b/tests/functional/arch/zxnext/divu16.asm @@ -0,0 +1,156 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld (_b), hl + ld hl, (_a) + srl h + rr l + ld (_b), hl + ld hl, (_a) + ld de, 4 + call .core.__DIVU16 + ld (_b), hl + ld hl, 0 + ld (_b), hl + ld hl, 1 + ld de, (_a) + call .core.__DIVU16 + ld (_b), hl + ld hl, 2 + ld de, (_a) + call .core.__DIVU16 + ld (_b), hl + ld hl, 4 + ld de, (_a) + call .core.__DIVU16 + ld (_b), hl + ld hl, (_a) + ld de, (_a) + call .core.__DIVU16 + ld (_b), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 41 "arch/zxnext/divu16.bas" + END diff --git a/tests/functional/arch/zxnext/divu16.bas b/tests/functional/arch/zxnext/divu16.bas new file mode 100644 index 000000000..dc3395036 --- /dev/null +++ b/tests/functional/arch/zxnext/divu16.bas @@ -0,0 +1,13 @@ +' TEST for DIVU16 + +DIM a as UInteger +DIM b as UInteger + +b = a / 1 +b = a / 2 +b = a / 4 +b = 0 / a +b = 1 / a +b = 2 / a +b = 4 / a +b = a / a diff --git a/tests/functional/arch/zxnext/divu16a.asm b/tests/functional/arch/zxnext/divu16a.asm new file mode 100644 index 000000000..92eb97fe2 --- /dev/null +++ b/tests/functional/arch/zxnext/divu16a.asm @@ -0,0 +1,132 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a) + call .core.__DIVU16 + ld de, (_a) + call .core.__DIVU16 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 19 "arch/zxnext/divu16a.bas" + END diff --git a/tests/functional/arch/zxnext/divu16a.bas b/tests/functional/arch/zxnext/divu16a.bas new file mode 100644 index 000000000..6dc894b38 --- /dev/null +++ b/tests/functional/arch/zxnext/divu16a.bas @@ -0,0 +1,4 @@ + +REM another DIVU16 test +DIM a As UInteger +a = a / a / a diff --git a/tests/functional/arch/zxnext/divu16b.asm b/tests/functional/arch/zxnext/divu16b.asm new file mode 100644 index 000000000..fb758ece6 --- /dev/null +++ b/tests/functional/arch/zxnext/divu16b.asm @@ -0,0 +1,139 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a) + call .core.__DIVU16 + push hl + ld hl, (_a) + srl h + rr l + ld de, (_a) + call .core.__DIVU16 + ex de, hl + pop hl + call .core.__DIVU16 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 26 "arch/zxnext/divu16b.bas" + END diff --git a/tests/functional/arch/zxnext/divu16b.bas b/tests/functional/arch/zxnext/divu16b.bas new file mode 100644 index 000000000..309f7fb45 --- /dev/null +++ b/tests/functional/arch/zxnext/divu16b.bas @@ -0,0 +1,4 @@ + +REM another DIVU16 test +DIM a As UInteger +a = a / a / (a / 2 / a) diff --git a/tests/functional/arch/zxnext/divu32c.asm b/tests/functional/arch/zxnext/divu32c.asm new file mode 100644 index 000000000..ff6876254 --- /dev/null +++ b/tests/functional/arch/zxnext/divu32c.asm @@ -0,0 +1,250 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 02h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__DIVU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__DIVU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__DIVU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__DIVU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 2 + push bc + call .core.__SWAP32 + call .core.__DIVU32 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 62 "arch/zxnext/divu32c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 63 "arch/zxnext/divu32c.bas" + END diff --git a/tests/functional/arch/zxnext/divu32c.bas b/tests/functional/arch/zxnext/divu32c.bas new file mode 100644 index 000000000..87cada97c --- /dev/null +++ b/tests/functional/arch/zxnext/divu32c.bas @@ -0,0 +1,9 @@ +dim level as Ulong = 1 +dim le as Ulong = 2 +dim l as Ulong + +l = le / (level + 0) +l = le / level +l = (le + 0) / level +l = (le + 0) / (level + 0) +l = 2 / (level + 0) diff --git a/tests/functional/arch/zxnext/divu8.asm b/tests/functional/arch/zxnext/divu8.asm new file mode 100644 index 000000000..225956ed6 --- /dev/null +++ b/tests/functional/arch/zxnext/divu8.asm @@ -0,0 +1,128 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld (_b), a + ld a, (_a) + srl a + ld (_b), a + ld a, (_a) + ld h, 4 + call .core.__DIVU8_FAST + ld (_b), a + xor a + ld (_b), a + ld a, 1 + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld (_b), a + ld a, 2 + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld (_b), a + ld a, 4 + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld (_b), a + ld a, (_a) + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 40 "arch/zxnext/divu8.bas" + END diff --git a/tests/functional/arch/zxnext/divu8.bas b/tests/functional/arch/zxnext/divu8.bas new file mode 100644 index 000000000..a8526bc53 --- /dev/null +++ b/tests/functional/arch/zxnext/divu8.bas @@ -0,0 +1,13 @@ +' TEST for Booleand AND 8 bits + +DIM a as Ubyte +DIM b as Ubyte + +b = a / 1 +b = a / 2 +b = a / 4 +b = 0 / a +b = 1 / a +b = 2 / a +b = 4 / a +b = a / a diff --git a/tests/functional/arch/zxnext/divu8a.asm b/tests/functional/arch/zxnext/divu8a.asm new file mode 100644 index 000000000..6b9739783 --- /dev/null +++ b/tests/functional/arch/zxnext/divu8a.asm @@ -0,0 +1,105 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 19 "arch/zxnext/divu8a.bas" + END diff --git a/tests/functional/arch/zxnext/divu8a.bas b/tests/functional/arch/zxnext/divu8a.bas new file mode 100644 index 000000000..532f027cc --- /dev/null +++ b/tests/functional/arch/zxnext/divu8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As UByte +a = a / a / a diff --git a/tests/functional/arch/zxnext/divu8b.asm b/tests/functional/arch/zxnext/divu8b.asm new file mode 100644 index 000000000..9d13c2d4b --- /dev/null +++ b/tests/functional/arch/zxnext/divu8b.asm @@ -0,0 +1,111 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__DIVU8_FAST + push af + ld a, (_a) + srl a + ld hl, (_a - 1) + call .core.__DIVU8_FAST + ld h, a + pop af + call .core.__DIVU8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 25 "arch/zxnext/divu8b.bas" + END diff --git a/tests/functional/arch/zxnext/divu8b.bas b/tests/functional/arch/zxnext/divu8b.bas new file mode 100644 index 000000000..bdae6477f --- /dev/null +++ b/tests/functional/arch/zxnext/divu8b.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As UByte +a = a / a / (a / 2 / a) diff --git a/tests/functional/arch/zxnext/lei32.asm b/tests/functional/arch/zxnext/lei32.asm new file mode 100644 index 000000000..46f8255be --- /dev/null +++ b/tests/functional/arch/zxnext/lei32.asm @@ -0,0 +1,185 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__SWAP32 + call .core.__LEI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__LEI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__LEI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__LEI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 0 + push bc + call .core.__LEI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/lei32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/lei32.asm" + push namespace core +__LEI32: ; Test 32 bit values Top of the stack <= HL,DE + PROC + LOCAL checkParity + exx + pop de ; Preserves return address + exx + call __SUB32 + exx + push de ; Puts return address back + exx + ex af, af' + ld a, h + or l + or e + or d + ld a, 1 + ret z + ex af, af' + jp po, checkParity + ld a, d + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 79 "arch/zxnext/lei32.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 80 "arch/zxnext/lei32.bas" + END diff --git a/tests/functional/arch/zxnext/lei32.bas b/tests/functional/arch/zxnext/lei32.bas new file mode 100644 index 000000000..6c1cbeacf --- /dev/null +++ b/tests/functional/arch/zxnext/lei32.bas @@ -0,0 +1,9 @@ +dim level as long = 1 +dim le as long = 0 +dim l as long + +l = le <= (level + 0) +l = le <= level +l = (le + 0) <= level +l = (le + 0) <= (level + 0) +l = 0 <= (level + 0) diff --git a/tests/functional/arch/zxnext/lei8.asm b/tests/functional/arch/zxnext/lei8.asm new file mode 100644 index 000000000..59dbb0659 --- /dev/null +++ b/tests/functional/arch/zxnext/lei8.asm @@ -0,0 +1,84 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h +_le: + DEFB 00h +_l: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_level) + ld h, a + ld a, (_le) + call .core.__LEI8 + ld (_l), a + ld hl, (_level - 1) + ld a, (_le) + call .core.__LEI8 + ld (_l), a + ld a, (_le) + push af + ld hl, (_level - 1) + pop af + call .core.__LEI8 + ld (_l), a + ld a, (_le) + ld hl, (_level - 1) + call .core.__LEI8 + ld (_l), a + ld a, (_level) + ld h, a + xor a + call .core.__LEI8 + ld (_l), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/lei8.asm" + push namespace core +__LEI8: ; Signed <= comparison for 8bit int + ; A <= H (registers) + PROC + LOCAL checkParity + sub h + jr nz, __LTI + inc a + ret +__LTI8: ; Test 8 bit values A < H + sub h +__LTI: ; Generic signed comparison + jp po, checkParity + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 37 "arch/zxnext/lei8.bas" + END diff --git a/tests/functional/arch/zxnext/lei8.bas b/tests/functional/arch/zxnext/lei8.bas new file mode 100644 index 000000000..38193ce3a --- /dev/null +++ b/tests/functional/arch/zxnext/lei8.bas @@ -0,0 +1,9 @@ +dim level as Byte = 1 +dim le as Byte = 0 +dim l as Byte + +l = le <= (level + 0) +l = le <= level +l = (le + 0) <= level +l = (le + 0) <= (level + 0) +l = 0 <= (level + 0) diff --git a/tests/functional/arch/zxnext/lti32c.asm b/tests/functional/arch/zxnext/lti32c.asm new file mode 100644 index 000000000..a96b3334f --- /dev/null +++ b/tests/functional/arch/zxnext/lti32c.asm @@ -0,0 +1,177 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__SWAP32 + call .core.__LTI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__LTI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__LTI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__LTI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 0 + push bc + call .core.__LTI32 + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/lti32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/lti32.asm" + push namespace core +__LTI32: ; Test 32 bit values in Top of the stack < HLDE + PROC + LOCAL checkParity + exx + pop de ; Preserves return address + exx + call __SUB32 + exx + push de ; Restores return address + exx + jp po, checkParity + ld a, d + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 79 "arch/zxnext/lti32c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 80 "arch/zxnext/lti32c.bas" + END diff --git a/tests/functional/arch/zxnext/lti32c.bas b/tests/functional/arch/zxnext/lti32c.bas new file mode 100644 index 000000000..311846907 --- /dev/null +++ b/tests/functional/arch/zxnext/lti32c.bas @@ -0,0 +1,9 @@ +dim level as long = 1 +dim le as long = 0 +dim l as long + +l = le < (level + 0) +l = le < level +l = (le + 0) < level +l = (le + 0) < (level + 0) +l = 0 < (level + 0) diff --git a/tests/functional/arch/zxnext/modf.asm b/tests/functional/arch/zxnext/modf.asm new file mode 100644 index 000000000..98994a2c7 --- /dev/null +++ b/tests/functional/arch/zxnext/modf.asm @@ -0,0 +1,159 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _a + 4 + call .core.__FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call .core.__MODF + ld hl, _a + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MODF: ; MODULO + call __FPSTACK_PUSH2 ; Enters B, A + ; ------------- ROM DIV + rst 28h + defb 01h ; EXCHANGE + defb 32h ; MOD + defb 02h ; Discard (POP) + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 21 "arch/zxnext/modf.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" + ; Routine to push Float pointed by HL + ; Into the stack. Notice that the hl points to the last + ; byte of the FP number. + ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers + push namespace core +__FP_PUSH_REV: + push hl + exx + pop hl + pop bc ; Return Address + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + push de + push bc ; Return Address + exx + ret + pop namespace +#line 22 "arch/zxnext/modf.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 23 "arch/zxnext/modf.bas" + END diff --git a/tests/functional/arch/zxnext/modf.bas b/tests/functional/arch/zxnext/modf.bas new file mode 100644 index 000000000..d5c52ff6f --- /dev/null +++ b/tests/functional/arch/zxnext/modf.bas @@ -0,0 +1,3 @@ +DIM a as Float + +LET a = a MOD 1 diff --git a/tests/functional/arch/zxnext/modf16.asm b/tests/functional/arch/zxnext/modf16.asm new file mode 100644 index 000000000..a56b2561a --- /dev/null +++ b/tests/functional/arch/zxnext/modf16.asm @@ -0,0 +1,40 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld hl, 0 + ld d, h + ld e, l + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/modf16.bas b/tests/functional/arch/zxnext/modf16.bas new file mode 100644 index 000000000..491bf7b5d --- /dev/null +++ b/tests/functional/arch/zxnext/modf16.bas @@ -0,0 +1,3 @@ +DIM a as Fixed + +LET a = a MOD 1 diff --git a/tests/functional/arch/zxnext/modf16c.asm b/tests/functional/arch/zxnext/modf16c.asm new file mode 100644 index 000000000..b753f795f --- /dev/null +++ b/tests/functional/arch/zxnext/modf16c.asm @@ -0,0 +1,443 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 02h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__SWAP32 + call .core.__MODF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__MODF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__MODF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__MODF16 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 2 + push bc + ld bc, 0 + push bc + call .core.__MODF16 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" + ; Computes A % B for fixed values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/divf16.asm" + push namespace core +__DIVF16: ; 16.16 Fixed point Division (signed) + ; DE.HL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + ex de, hl ; D'E'.H'L' Dividend +__DIVF16START: ; FAST Entry: DEHL => Dividend, D'E'H'L' => Divisor + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with D'E'.H'L' + ex af, af' + xor d + ex af, af' ; Stores sign of the result for later + bit 7, d ; Negative? + call nz, __NEG32 + exx ; Now we have DE.HL => Dividend + ld b, 16 +__SHIFTALOOP: ; Tries to shift Dividend to the left + bit 7, d + jp nz, __SHIFTB + add hl, hl + ex de, hl + adc hl, hl + ex de, hl + djnz __SHIFTALOOP + jp __DOF16_DIVRDY +__SHIFTB: ; Cannot shift Dividend more to the left, try to shift Divisor to the right + ld a, b + exx + ld b, a + ; Divisor is in DEHL +__SHIFTBLOOP: + bit 1, l + jp nz, __DOF16_DIVIDE + sra d + rr e + rr h + rr l + djnz __SHIFTBLOOP +__DOF16_DIVIDE: + ld a, b + exx + ld b, a +__DOF16_DIVRDY: + exx + ex de, hl + push bc + call __DIVU32START + pop bc + xor a + or b + jp z, __ENDF16DIV +__SHIFTCLOOP: + add hl, hl ; Shift DECIMAL PART << 1 + ex de, hl + adc hl, hl ; Shift INTEGER PART << 1 Plus Carry + ex de, hl + djnz __SHIFTCLOOP +__ENDF16DIV: ; Put the sign on the result + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there + pop namespace +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" + push namespace core +__MULF16: ; + ld a, d ; load sgn into a + ex af, af' ; saves it + call __ABS32 ; convert to positive + exx + pop hl ; Return address + pop de ; Low part + ex (sp), hl ; CALLEE caller convention; Now HL = Hight part, (SP) = Return address + ex de, hl ; D'E' = High part (B), H'L' = Low part (B) (must be in DE) + ex af, af' + xor d ; A register contains resulting sgn + ex af, af' + call __ABS32 ; convert to positive + call __MUL32_64START + ; rounding (was not included in zx81) +__ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 + ; result returned in dehl + ; input in h'l'hlb'c'ac + sla a ; result bit 47 to carry + exx + ld hl,0 ; ld does not change carry + adc hl,bc ; hl = hl + 0 + carry + push hl + exx + ld bc,0 + adc hl,bc ; hl = hl + 0 + carry + ex de, hl + pop hl ; rounded result in de.hl + ex af, af' ; recovers result sign + or a + jp m, __NEG32 ; if negative, negates it + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/modf16.asm" + push namespace core +__MODF16: + ; 16.16 Fixed point Division (signed) + ; DE.HL = Divisor, Stack Top = Divider + ; A = Dividend, B = Divisor => A % B + PROC + LOCAL TEMP + TEMP EQU 23698 ; MEMBOT + pop bc ; ret addr + ld (TEMP), bc ; stores it on MEMBOT temporarily + ld (TEMP + 2), hl ; stores HP of divider + ld (TEMP + 4), de ; stores DE of divider + call __DIVF16 + rlc d ; Sign into carry + sbc a, a ; a register = -1 sgn(DE), or 0 + ld d, a + ld e, a ; DE = 0 if it was positive or 0; -1 if it was negative + ld bc, (TEMP + 4) ; Pushes original divider into the stack + push bc + ld bc, (TEMP + 2) + push bc + ld bc, (TEMP) ; recovers return address + push bc + jp __MULF16 ; multiplies and return from there + ENDP + pop namespace +#line 59 "arch/zxnext/modf16c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 60 "arch/zxnext/modf16c.bas" + END diff --git a/tests/functional/arch/zxnext/modf16c.bas b/tests/functional/arch/zxnext/modf16c.bas new file mode 100644 index 000000000..5437b62a5 --- /dev/null +++ b/tests/functional/arch/zxnext/modf16c.bas @@ -0,0 +1,9 @@ +dim level as fixed = 1 +dim le as fixed = 2 +dim l as fixed + +l = le mod (level + 0) +l = le mod level +l = (le + 0) mod level +l = (le + 0) mod (level + 0) +l = 2 mod (level + 0) diff --git a/tests/functional/arch/zxnext/modi32c.asm b/tests/functional/arch/zxnext/modi32c.asm new file mode 100644 index 000000000..0b68bc1aa --- /dev/null +++ b/tests/functional/arch/zxnext/modi32c.asm @@ -0,0 +1,250 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 02h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__MODI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__MODI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__MODI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__MODI32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 2 + push bc + call .core.__SWAP32 + call .core.__MODI32 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 62 "arch/zxnext/modi32c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 63 "arch/zxnext/modi32c.bas" + END diff --git a/tests/functional/arch/zxnext/modi32c.bas b/tests/functional/arch/zxnext/modi32c.bas new file mode 100644 index 000000000..1b2b018a4 --- /dev/null +++ b/tests/functional/arch/zxnext/modi32c.bas @@ -0,0 +1,9 @@ +dim level as long = 1 +dim le as long = 2 +dim l as long + +l = le mod (level + 0) +l = le mod level +l = (le + 0) mod level +l = (le + 0) mod (level + 0) +l = 2 mod (level + 0) diff --git a/tests/functional/arch/zxnext/modi8.asm b/tests/functional/arch/zxnext/modi8.asm new file mode 100644 index 000000000..b18224a09 --- /dev/null +++ b/tests/functional/arch/zxnext/modi8.asm @@ -0,0 +1,127 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + xor a + ld (_b), a + ld a, (_a) + and 1 + ld (_b), a + ld a, (_a) + and 3 + ld (_b), a + xor a + ld (_b), a + ld a, 1 + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld (_b), a + ld a, 2 + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld (_b), a + ld a, 4 + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld (_b), a + ld a, (_a) + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 39 "arch/zxnext/modi8.bas" + END diff --git a/tests/functional/arch/zxnext/modi8.bas b/tests/functional/arch/zxnext/modi8.bas new file mode 100644 index 000000000..df2aad4f7 --- /dev/null +++ b/tests/functional/arch/zxnext/modi8.bas @@ -0,0 +1,13 @@ +' TEST for MOD 8 bits + +DIM a as Byte +DIM b as Byte + +b = a mod 1 +b = a mod 2 +b = a mod 4 +b = 0 mod a +b = 1 mod a +b = 2 mod a +b = 4 mod a +b = a mod a diff --git a/tests/functional/arch/zxnext/modi8a.asm b/tests/functional/arch/zxnext/modi8a.asm new file mode 100644 index 000000000..873ce1b60 --- /dev/null +++ b/tests/functional/arch/zxnext/modi8a.asm @@ -0,0 +1,105 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 19 "arch/zxnext/modi8a.bas" + END diff --git a/tests/functional/arch/zxnext/modi8a.bas b/tests/functional/arch/zxnext/modi8a.bas new file mode 100644 index 000000000..9d0447303 --- /dev/null +++ b/tests/functional/arch/zxnext/modi8a.bas @@ -0,0 +1,4 @@ + +REM another MOD8 tests +DIM a As Byte +a = a mod a mod a diff --git a/tests/functional/arch/zxnext/modi8b.asm b/tests/functional/arch/zxnext/modi8b.asm new file mode 100644 index 000000000..1ccfb2a59 --- /dev/null +++ b/tests/functional/arch/zxnext/modi8b.asm @@ -0,0 +1,111 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__MODI8_FAST + push af + ld a, (_a) + and 1 + ld hl, (_a - 1) + call .core.__MODI8_FAST + ld h, a + pop af + call .core.__MODI8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 25 "arch/zxnext/modi8b.bas" + END diff --git a/tests/functional/arch/zxnext/modi8b.bas b/tests/functional/arch/zxnext/modi8b.bas new file mode 100644 index 000000000..e23eed1ca --- /dev/null +++ b/tests/functional/arch/zxnext/modi8b.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a mod a mod (a mod 2 mod a) diff --git a/tests/functional/arch/zxnext/modu32c.asm b/tests/functional/arch/zxnext/modu32c.asm new file mode 100644 index 000000000..9f3f14a4c --- /dev/null +++ b/tests/functional/arch/zxnext/modu32c.asm @@ -0,0 +1,250 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 02h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__MODU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__MODU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__MODU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SWAP32 + call .core.__MODU32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 2 + push bc + call .core.__SWAP32 + call .core.__MODU32 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 62 "arch/zxnext/modu32c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 63 "arch/zxnext/modu32c.bas" + END diff --git a/tests/functional/arch/zxnext/modu32c.bas b/tests/functional/arch/zxnext/modu32c.bas new file mode 100644 index 000000000..1bcaebc3f --- /dev/null +++ b/tests/functional/arch/zxnext/modu32c.bas @@ -0,0 +1,9 @@ +dim level as ulong = 1 +dim le as ulong = 2 +dim l as ulong + +l = le mod (level + 0) +l = le mod level +l = (le + 0) mod level +l = (le + 0) mod (level + 0) +l = 2 mod (level + 0) diff --git a/tests/functional/arch/zxnext/modu8.asm b/tests/functional/arch/zxnext/modu8.asm new file mode 100644 index 000000000..f4e27b810 --- /dev/null +++ b/tests/functional/arch/zxnext/modu8.asm @@ -0,0 +1,127 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + xor a + ld (_b), a + ld a, (_a) + and 1 + ld (_b), a + ld a, (_a) + and 3 + ld (_b), a + xor a + ld (_b), a + ld a, 1 + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld (_b), a + ld a, 2 + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld (_b), a + ld a, 4 + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld (_b), a + ld a, (_a) + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 39 "arch/zxnext/modu8.bas" + END diff --git a/tests/functional/arch/zxnext/modu8.bas b/tests/functional/arch/zxnext/modu8.bas new file mode 100644 index 000000000..fb8777395 --- /dev/null +++ b/tests/functional/arch/zxnext/modu8.bas @@ -0,0 +1,13 @@ +' TEST for MOD 8 bits + +DIM a as Ubyte +DIM b as Ubyte + +b = a mod 1 +b = a mod 2 +b = a mod 4 +b = 0 mod a +b = 1 mod a +b = 2 mod a +b = 4 mod a +b = a mod a diff --git a/tests/functional/arch/zxnext/modu8a.asm b/tests/functional/arch/zxnext/modu8a.asm new file mode 100644 index 000000000..a2193e96d --- /dev/null +++ b/tests/functional/arch/zxnext/modu8a.asm @@ -0,0 +1,105 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 19 "arch/zxnext/modu8a.bas" + END diff --git a/tests/functional/arch/zxnext/modu8a.bas b/tests/functional/arch/zxnext/modu8a.bas new file mode 100644 index 000000000..5b8038ede --- /dev/null +++ b/tests/functional/arch/zxnext/modu8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As UByte +a = a mod a mod a diff --git a/tests/functional/arch/zxnext/modu8b.asm b/tests/functional/arch/zxnext/modu8b.asm new file mode 100644 index 000000000..ff2872244 --- /dev/null +++ b/tests/functional/arch/zxnext/modu8b.asm @@ -0,0 +1,111 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + call .core.__MODU8_FAST + push af + ld a, (_a) + and 1 + ld hl, (_a - 1) + call .core.__MODU8_FAST + ld h, a + pop af + call .core.__MODU8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 25 "arch/zxnext/modu8b.bas" + END diff --git a/tests/functional/arch/zxnext/modu8b.bas b/tests/functional/arch/zxnext/modu8b.bas new file mode 100644 index 000000000..0cfe81f02 --- /dev/null +++ b/tests/functional/arch/zxnext/modu8b.bas @@ -0,0 +1,4 @@ + +REM another MOD8 tests +DIM a As UByte +a = a mod a mod (a mod 2 mod a) diff --git a/tests/functional/arch/zxnext/mul16.asm b/tests/functional/arch/zxnext/mul16.asm new file mode 100644 index 000000000..922640566 --- /dev/null +++ b/tests/functional/arch/zxnext/mul16.asm @@ -0,0 +1,76 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld (_b), hl + ld hl, (_a) + ld (_b), hl + ld hl, 0 + ld (_b), hl + ld hl, (_a) + ld (_b), hl + ld de, (_a) + ld hl, (_a) + call .core.__MUL16_FAST + ld (_b), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 25 "arch/zxnext/mul16.bas" + END diff --git a/tests/functional/arch/zxnext/mul16.bas b/tests/functional/arch/zxnext/mul16.bas new file mode 100644 index 000000000..8cdb72ac1 --- /dev/null +++ b/tests/functional/arch/zxnext/mul16.bas @@ -0,0 +1,10 @@ +' TEST for ADD16 + +DIM a as UInteger +DIM b as UInteger + +b = a * 0 +b = a * 1 +b = 0 * a +b = 1 * a +b = a * a diff --git a/tests/functional/arch/zxnext/mul16a.asm b/tests/functional/arch/zxnext/mul16a.asm new file mode 100644 index 000000000..84aa20adb --- /dev/null +++ b/tests/functional/arch/zxnext/mul16a.asm @@ -0,0 +1,69 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, (_a) + ld hl, (_a) + call .core.__MUL16_FAST + ex de, hl + ld hl, (_a) + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 20 "arch/zxnext/mul16a.bas" + END diff --git a/tests/functional/arch/zxnext/mul16a.bas b/tests/functional/arch/zxnext/mul16a.bas new file mode 100644 index 000000000..15611ed0b --- /dev/null +++ b/tests/functional/arch/zxnext/mul16a.bas @@ -0,0 +1,4 @@ + +REM another MUL16 test +DIM a As Integer +a = a * a * a diff --git a/tests/functional/arch/zxnext/mul16b.asm b/tests/functional/arch/zxnext/mul16b.asm new file mode 100644 index 000000000..8e35b9176 --- /dev/null +++ b/tests/functional/arch/zxnext/mul16b.asm @@ -0,0 +1,75 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, (_a) + ld hl, (_a) + call .core.__MUL16_FAST + push hl + ld hl, (_a) + add hl, hl + ex de, hl + ld hl, (_a) + call .core.__MUL16_FAST + ex de, hl + pop hl + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 26 "arch/zxnext/mul16b.bas" + END diff --git a/tests/functional/arch/zxnext/mul16b.bas b/tests/functional/arch/zxnext/mul16b.bas new file mode 100644 index 000000000..1d849dff4 --- /dev/null +++ b/tests/functional/arch/zxnext/mul16b.bas @@ -0,0 +1,4 @@ + +REM another ADD16 tests +DIM a As Integer +a = a * a * (a * 2 * a) diff --git a/tests/functional/arch/zxnext/mul16c.asm b/tests/functional/arch/zxnext/mul16c.asm new file mode 100644 index 000000000..cf07644d6 --- /dev/null +++ b/tests/functional/arch/zxnext/mul16c.asm @@ -0,0 +1,66 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, -1 + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mul16.asm" + push namespace core +__MUL16: ; Mutiplies HL with the last value stored into de stack + ; Works for both signed and unsigned + PROC + ex de, hl + pop hl ; Return address + ex (sp), hl ; CALLEE caller convention +__MUL16_FAST: + ld a,d ; a = xh + ld d,h ; d = yh + ld h,a ; h = xh + ld c,e ; c = xl + ld b,l ; b = yl + mul d,e ; yh * yl + ex de,hl + mul d,e ; xh * yl + add hl,de ; add cross products + ld e,c + ld d,b + mul d,e ; yl * xl + ld a,l ; cross products lsb + add a,d ; add to msb final + ld h,a + ld l,e ; hl = final + ret ; Result in hl (16 lower bits) + ENDP + pop namespace +#line 17 "arch/zxnext/mul16c.bas" + END diff --git a/tests/functional/arch/zxnext/mul16c.bas b/tests/functional/arch/zxnext/mul16c.bas new file mode 100644 index 000000000..6462127b8 --- /dev/null +++ b/tests/functional/arch/zxnext/mul16c.bas @@ -0,0 +1,3 @@ +DIM a as Integer + +a = -1 * a diff --git a/tests/functional/arch/zxnext/mul8.asm b/tests/functional/arch/zxnext/mul8.asm new file mode 100644 index 000000000..f311e33a3 --- /dev/null +++ b/tests/functional/arch/zxnext/mul8.asm @@ -0,0 +1,66 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + xor a + ld (_b), a + ld a, (_a) + ld (_b), a + ld a, (_a) + add a, a + ld (_b), a + ld a, (_a) + add a, a + add a, a + ld (_b), a + ld a, (_a) + xor a + ld (_b), a + ld a, (_a) + ld (_b), a + ld a, (_a) + add a, a + ld (_b), a + ld a, (_a) + add a, a + add a, a + ld (_b), a + ld hl, (_a - 1) + ld a, (_a) + ld d, h + ld e, a + mul d, e + ld a, e + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/mul8.bas b/tests/functional/arch/zxnext/mul8.bas new file mode 100644 index 000000000..90671ea95 --- /dev/null +++ b/tests/functional/arch/zxnext/mul8.bas @@ -0,0 +1,14 @@ +' TEST for Booleand AND 8 bits + +DIM a as Ubyte +DIM b as Ubyte + +b = a * 0 +b = a * 1 +b = a * 2 +b = a * 4 +b = 0 * a +b = 1 * a +b = 2 * a +b = 4 * a +b = a * a diff --git a/tests/functional/arch/zxnext/mul8a.asm b/tests/functional/arch/zxnext/mul8a.asm new file mode 100644 index 000000000..ab49568e0 --- /dev/null +++ b/tests/functional/arch/zxnext/mul8a.asm @@ -0,0 +1,45 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a - 1) + ld a, (_a) + ld d, h + ld e, a + mul d, e + ld h, e + ld a, (_a) + ld d, h + ld e, a + mul d, e + ld a, e + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/mul8a.bas b/tests/functional/arch/zxnext/mul8a.bas new file mode 100644 index 000000000..cbe63126e --- /dev/null +++ b/tests/functional/arch/zxnext/mul8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a * a * a diff --git a/tests/functional/arch/zxnext/mul8b.asm b/tests/functional/arch/zxnext/mul8b.asm new file mode 100644 index 000000000..ddf1f9e59 --- /dev/null +++ b/tests/functional/arch/zxnext/mul8b.asm @@ -0,0 +1,53 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a - 1) + ld a, (_a) + ld d, h + ld e, a + mul d, e + ld a, e + push af + ld a, (_a) + add a, a + ld h, a + ld a, (_a) + ld d, h + ld e, a + mul d, e + pop af + ld d, e + ld e, a + mul d, e + ld a, e + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/mul8b.bas b/tests/functional/arch/zxnext/mul8b.bas new file mode 100644 index 000000000..11c8da54d --- /dev/null +++ b/tests/functional/arch/zxnext/mul8b.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a * a * (a * 2 * a) diff --git a/tests/functional/arch/zxnext/mulf00.asm b/tests/functional/arch/zxnext/mulf00.asm new file mode 100644 index 000000000..fef7656fb --- /dev/null +++ b/tests/functional/arch/zxnext/mulf00.asm @@ -0,0 +1,167 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 80h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_b: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _b + 4 + call .core.__FP_PUSH_REV + ld a, 082h + ld de, 00000h + ld bc, 00000h + call .core.__MULF + ld hl, _b + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 21 "arch/zxnext/mulf00.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" + ; Routine to push Float pointed by HL + ; Into the stack. Notice that the hl points to the last + ; byte of the FP number. + ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers + push namespace core +__FP_PUSH_REV: + push hl + exx + pop hl + pop bc ; Return Address + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + push de + push bc ; Return Address + exx + ret + pop namespace +#line 22 "arch/zxnext/mulf00.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 23 "arch/zxnext/mulf00.bas" + END diff --git a/tests/functional/arch/zxnext/mulf00.bas b/tests/functional/arch/zxnext/mulf00.bas new file mode 100644 index 000000000..57211f66c --- /dev/null +++ b/tests/functional/arch/zxnext/mulf00.bas @@ -0,0 +1,4 @@ +DIM a as FLOAT = 0.5 +DIM b as FLOAT = 1 + +b = b * 2 diff --git a/tests/functional/arch/zxnext/mulf01.asm b/tests/functional/arch/zxnext/mulf01.asm new file mode 100644 index 000000000..0c16be709 --- /dev/null +++ b/tests/functional/arch/zxnext/mulf01.asm @@ -0,0 +1,143 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 80h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_b: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld de, (_b + 1) + ld bc, (_b + 3) + ld hl, 00000h + push hl + ld hl, 00000h + push hl + ld h, 082h + push hl + call .core.__MULF + ld hl, _b + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__MULF: ; Multiplication + call __FPSTACK_PUSH2 + ; ------------- ROM MUL + rst 28h + defb 04h ; + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 25 "arch/zxnext/mulf01.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 26 "arch/zxnext/mulf01.bas" + END diff --git a/tests/functional/arch/zxnext/mulf01.bas b/tests/functional/arch/zxnext/mulf01.bas new file mode 100644 index 000000000..95ab7e941 --- /dev/null +++ b/tests/functional/arch/zxnext/mulf01.bas @@ -0,0 +1,4 @@ +DIM a as FLOAT = 0.5 +DIM b as FLOAT = 1 + +b = 2 * b diff --git a/tests/functional/arch/zxnext/mulf16.asm b/tests/functional/arch/zxnext/mulf16.asm new file mode 100644 index 000000000..484e245ff --- /dev/null +++ b/tests/functional/arch/zxnext/mulf16.asm @@ -0,0 +1,188 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +_b: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a + 2) + ld hl, 0 + ld e, h + ld d, l + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld hl, 0 + ld e, h + ld d, l + ld (_b), hl + ld (_b + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_b), hl + ld (_b + 2), de + ld hl, (_a + 2) + push hl + ld hl, (_a) + push hl + ld hl, (_a) + ld de, (_a + 2) + call .core.__MULF16 + ld (_b), hl + ld (_b + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" + push namespace core +__MULF16: ; + ld a, d ; load sgn into a + ex af, af' ; saves it + call __ABS32 ; convert to positive + exx + pop hl ; Return address + pop de ; Low part + ex (sp), hl ; CALLEE caller convention; Now HL = Hight part, (SP) = Return address + ex de, hl ; D'E' = High part (B), H'L' = Low part (B) (must be in DE) + ex af, af' + xor d ; A register contains resulting sgn + ex af, af' + call __ABS32 ; convert to positive + call __MUL32_64START + ; rounding (was not included in zx81) +__ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 + ; result returned in dehl + ; input in h'l'hlb'c'ac + sla a ; result bit 47 to carry + exx + ld hl,0 ; ld does not change carry + adc hl,bc ; hl = hl + 0 + carry + push hl + exx + ld bc,0 + adc hl,bc ; hl = hl + 0 + carry + ex de, hl + pop hl ; rounded result in de.hl + ex af, af' ; recovers result sign + or a + jp m, __NEG32 ; if negative, negates it + ret + pop namespace +#line 44 "arch/zxnext/mulf16.bas" + END diff --git a/tests/functional/arch/zxnext/mulf16.bas b/tests/functional/arch/zxnext/mulf16.bas new file mode 100644 index 000000000..06cc5323c --- /dev/null +++ b/tests/functional/arch/zxnext/mulf16.bas @@ -0,0 +1,10 @@ +' TEST for Fixed + +DIM a as Fixed +DIM b as Fixed + +b = a * 0 +b = a * 1 +b = 0 * a +b = 1 * a +b = a * a diff --git a/tests/functional/arch/zxnext/mulf16a.asm b/tests/functional/arch/zxnext/mulf16a.asm new file mode 100644 index 000000000..a6874b617 --- /dev/null +++ b/tests/functional/arch/zxnext/mulf16a.asm @@ -0,0 +1,172 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00h + DEFB 80h + DEFB 00h + DEFB 00h +_b: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_b + 2) + push hl + ld hl, (_b) + push hl + ld de, 2 + ld hl, 0 + call .core.__MULF16 + ld (_b), hl + ld (_b + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/_mul32.asm" +; Ripped from: http://www.andreadrian.de/oldcpu/z80_number_cruncher.html#moztocid784223 + ; Used with permission. + ; Multiplies 32x32 bit integer (DEHL x D'E'H'L') + ; 64bit result is returned in H'L'H L B'C'A C + push namespace core +__MUL32_64START: + push hl + exx + ld b, h + ld c, l ; BC = Low Part (A) + pop hl ; HL = Load Part (B) + ex de, hl ; DE = Low Part (B), HL = HightPart(A) (must be in B'C') + push hl + exx + pop bc ; B'C' = HightPart(A) + exx ; A = B'C'BC , B = D'E'DE + ; multiply routine 32 * 32bit = 64bit + ; h'l'hlb'c'ac = b'c'bc * d'e'de + ; needs register a, changes flags + ; + ; this routine was with tiny differences in the + ; sinclair zx81 rom for the mantissa multiply +__LMUL: + xor a ; reset carry flag + ld h, a ; result bits 32..47 = 0 + ld l, a + exx + ld h, a ; result bits 48..63 = 0 + ld l, a + exx + ld a,b ; mpr is b'c'ac + ld b,33 ; initialize loop counter + jp __LMULSTART +__LMULLOOP: + jr nc,__LMULNOADD ; JP is 2 cycles faster than JR. Since it's inside a LOOP + ; it can save up to 33 * 2 = 66 cycles + ; But JR if 3 cycles faster if JUMP not taken! + add hl,de ; result += mpd + exx + adc hl,de + exx +__LMULNOADD: + exx + rr h ; right shift upper + rr l ; 32bit of result + exx + rr h + rr l +__LMULSTART: + exx + rr b ; right shift mpr/ + rr c ; lower 32bit of result + exx + rra ; equivalent to rr a + rr c + djnz __LMULLOOP + ret ; result in h'l'hlb'c'ac + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/arith/mulf16.asm" + push namespace core +__MULF16: ; + ld a, d ; load sgn into a + ex af, af' ; saves it + call __ABS32 ; convert to positive + exx + pop hl ; Return address + pop de ; Low part + ex (sp), hl ; CALLEE caller convention; Now HL = Hight part, (SP) = Return address + ex de, hl ; D'E' = High part (B), H'L' = Low part (B) (must be in DE) + ex af, af' + xor d ; A register contains resulting sgn + ex af, af' + call __ABS32 ; convert to positive + call __MUL32_64START + ; rounding (was not included in zx81) +__ROUND_FIX: ; rounds a 64bit (32.32) fixed point number to 16.16 + ; result returned in dehl + ; input in h'l'hlb'c'ac + sla a ; result bit 47 to carry + exx + ld hl,0 ; ld does not change carry + adc hl,bc ; hl = hl + 0 + carry + push hl + exx + ld bc,0 + adc hl,bc ; hl = hl + 0 + carry + ex de, hl + pop hl ; rounded result in de.hl + ex af, af' ; recovers result sign + or a + jp m, __NEG32 ; if negative, negates it + ret + pop namespace +#line 22 "arch/zxnext/mulf16a.bas" + END diff --git a/tests/functional/arch/zxnext/mulf16a.bas b/tests/functional/arch/zxnext/mulf16a.bas new file mode 100644 index 000000000..ed503c75f --- /dev/null +++ b/tests/functional/arch/zxnext/mulf16a.bas @@ -0,0 +1,4 @@ +DIM a as FIXED = 0.5 +DIM b as FIXED = 1 + +b = b * 2 diff --git a/tests/functional/arch/zxnext/print_f.asm b/tests/functional/arch/zxnext/print_f.asm new file mode 100644 index 000000000..a5faf6e47 --- /dev/null +++ b/tests/functional/arch/zxnext/print_f.asm @@ -0,0 +1,1340 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__MEM_INIT + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + call .core.__PRINTF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 18 "arch/zxnext/print_f.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printstr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/heapinit.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; + ; This ASM library is licensed under the BSD license + ; you can use it for any purpose (even for commercial + ; closed source programs). + ; + ; Please read the BSD license on the internet + ; ----- IMPLEMENTATION NOTES ------ + ; The heap is implemented as a linked list of free blocks. +; Each free block contains this info: + ; + ; +----------------+ <-- HEAP START + ; | Size (2 bytes) | + ; | 0 | <-- Size = 0 => DUMMY HEADER BLOCK + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | <-- If Size > 4, then this contains (size - 4) bytes + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ | + ; | <-- This zone is in use (Already allocated) + ; +----------------+ <-+ + ; | Size (2 bytes) | + ; +----------------+ + ; | Next (2 bytes) |---+ + ; +----------------+ | + ; | | | + ; | (0 if Size = 4)| | + ; +----------------+ <-+ + ; | Next (2 bytes) |--> NULL => END OF LIST + ; | 0 = NULL | + ; +----------------+ + ; | | + ; | (0 if Size = 4)| + ; +----------------+ + ; When a block is FREED, the previous and next pointers are examined to see + ; if we can defragment the heap. If the block to be breed is just next to the + ; previous, or to the next (or both) they will be converted into a single + ; block (so defragmented). + ; MEMORY MANAGER + ; + ; This library must be initialized calling __MEM_INIT with + ; HL = BLOCK Start & DE = Length. + ; An init directive is useful for initialization routines. + ; They will be added automatically if needed. + ; --------------------------------------------------------------------- + ; __MEM_INIT must be called to initalize this library with the + ; standard parameters + ; --------------------------------------------------------------------- + push namespace core +__MEM_INIT: ; Initializes the library using (RAMTOP) as start, and + ld hl, ZXBASIC_MEM_HEAP ; Change this with other address of heap start + ld de, ZXBASIC_HEAP_SIZE ; Change this with your size + ; --------------------------------------------------------------------- + ; __MEM_INIT2 initalizes this library +; Parameters: +; HL : Memory address of 1st byte of the memory heap +; DE : Length in bytes of the Memory Heap + ; --------------------------------------------------------------------- +__MEM_INIT2: + ; HL as TOP + PROC + dec de + dec de + dec de + dec de ; DE = length - 4; HL = start + ; This is done, because we require 4 bytes for the empty dummy-header block + xor a + ld (hl), a + inc hl + ld (hl), a ; First "free" block is a header: size=0, Pointer=&(Block) + 4 + inc hl + ld b, h + ld c, l + inc bc + inc bc ; BC = starts of next block + ld (hl), c + inc hl + ld (hl), b + inc hl ; Pointer to next block + ld (hl), e + inc hl + ld (hl), d + inc hl ; Block size (should be length - 4 at start); This block contains all the available memory + ld (hl), a ; NULL (0000h) ; No more blocks (a list with a single block) + inc hl + ld (hl), a + ld a, 201 + ld (__MEM_INIT), a; "Pokes" with a RET so ensure this routine is not called again + ret + ENDP + pop namespace +#line 69 "/zxbasic/src/lib/arch/zxnext/runtime/free.asm" + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/printstr.asm" + ; PRINT command routine + ; Prints string pointed by HL + push namespace core +PRINT_STR: +__PRINTSTR: ; __FASTCALL__ Entry to print_string + PROC + LOCAL __PRINT_STR_LOOP + LOCAL __PRINT_STR_END + ld d, a ; Saves A reg (Flag) for later + ld a, h + or l + ret z ; Return if the pointer is NULL + push hl + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = LEN(a$); HL = &a$ +__PRINT_STR_LOOP: + ld a, b + or c + jr z, __PRINT_STR_END ; END if BC (counter = 0) + ld a, (hl) + call __PRINTCHAR + inc hl + dec bc + jp __PRINT_STR_LOOP +__PRINT_STR_END: + pop hl + ld a, d ; Recovers A flag + or a ; If not 0 this is a temporary string. Free it + ret z + jp __MEM_FREE ; Frees str from heap and return from there +__PRINT_STR: + ; Fastcall Entry + ; It ONLY prints strings + ; HL = String start + ; BC = String length (Number of chars) + push hl ; Push str address for later + ld d, a ; Saves a FLAG + jp __PRINT_STR_LOOP + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printf.asm" + push namespace core +__PRINTF: ; Prints a Fixed point Number stored in C ED LH + PROC + LOCAL RECLAIM2 + LOCAL STK_END + STK_END EQU 5C65h + ld hl, (ATTR_T) + push hl ; Saves ATTR_T since BUG ROM changes it + ld hl, (STK_END) + push hl ; Stores STK_END + call __FPSTACK_PUSH ; Push number into stack + rst 28h ; # Rom Calculator + defb 2Eh ; # STR$(x) + defb 38h ; # END CALC + call __FPSTACK_POP ; Recovers string parameters to A ED CB + pop hl + ld (STK_END), hl ; Balance STK_END to avoid STR$ bug + pop hl + ld (ATTR_T), hl ; Restores ATTR_T + ex de, hl ; String position now in HL + push bc + xor a ; Avoid the str to be FREED from heap + call __PRINT_STR + pop bc + inc bc + jp RECLAIM2 ; Frees TMP Memory + RECLAIM2 EQU 19E8h + ENDP + pop namespace +#line 19 "arch/zxnext/print_f.bas" + END diff --git a/tests/functional/arch/zxnext/print_f.bas b/tests/functional/arch/zxnext/print_f.bas new file mode 100644 index 000000000..2a5d6a782 --- /dev/null +++ b/tests/functional/arch/zxnext/print_f.bas @@ -0,0 +1,2 @@ +DIM a as Float +PRINT a; diff --git a/tests/functional/arch/zxnext/print_i16.asm b/tests/functional/arch/zxnext/print_i16.asm new file mode 100644 index 000000000..11bf1e37f --- /dev/null +++ b/tests/functional/arch/zxnext/print_i16.asm @@ -0,0 +1,1097 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld hl, (_a) + call .core.__PRINTI16 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 16 "arch/zxnext/print_i16.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" + push namespace core +__PRINTI16: ; Prints a 16bits signed in HL + ; Converts 16 to 32 bits + PROC + LOCAL __PRINTU_LOOP + ld a, h + or a + jp p, __PRINTU16 + call __PRINT_MINUS + call __NEGHL +__PRINTU16: + ld b, 0 +__PRINTU_LOOP: + ld a, h + or l + jp z, __PRINTU_START + push bc + ld de, 10 + call __DIVU16_FAST ; Divides by DE. DE = MODULUS at exit. Since < 256, E = Modulus + pop bc + ld a, e + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 17 "arch/zxnext/print_i16.bas" + END diff --git a/tests/functional/arch/zxnext/print_i16.bas b/tests/functional/arch/zxnext/print_i16.bas new file mode 100644 index 000000000..62834fbab --- /dev/null +++ b/tests/functional/arch/zxnext/print_i16.bas @@ -0,0 +1,2 @@ +DIM a as Integer +PRINT a; diff --git a/tests/functional/arch/zxnext/print_i32.asm b/tests/functional/arch/zxnext/print_i32.asm new file mode 100644 index 000000000..5d4e904fc --- /dev/null +++ b/tests/functional/arch/zxnext/print_i32.asm @@ -0,0 +1,1151 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld hl, (_a) + ld de, (_a + 2) + call .core.__PRINTI32 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 17 "arch/zxnext/print_i32.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" + push namespace core +__PRINTI32: + ld a, d + or a + jp p, __PRINTU32 + call __PRINT_MINUS + call __NEG32 +__PRINTU32: + PROC + LOCAL __PRINTU_LOOP + ld b, 0 ; Counter +__PRINTU_LOOP: + ld a, h + or l + or d + or e + jp z, __PRINTU_START + push bc + ld bc, 0 + push bc + ld bc, 10 + push bc ; Push 00 0A (10 Dec) into the stack = divisor + call __DIVU32 ; Divides by 32. D'E'H'L' contains modulo (L' since < 10) + pop bc + exx + ld a, l + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + exx + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 18 "arch/zxnext/print_i32.bas" + END diff --git a/tests/functional/arch/zxnext/print_i32.bas b/tests/functional/arch/zxnext/print_i32.bas new file mode 100644 index 000000000..37dc14598 --- /dev/null +++ b/tests/functional/arch/zxnext/print_i32.bas @@ -0,0 +1,2 @@ +DIM a as Long +PRINT a; diff --git a/tests/functional/arch/zxnext/print_i8.asm b/tests/functional/arch/zxnext/print_i8.asm new file mode 100644 index 000000000..683f78e0b --- /dev/null +++ b/tests/functional/arch/zxnext/print_i8.asm @@ -0,0 +1,1071 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_a) + call .core.__PRINTI8 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 16 "arch/zxnext/print_i8.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" + push namespace core +__PRINTI8: ; Prints an 8 bits number in Accumulator (A) + ; Converts 8 to 32 bits + or a + jp p, __PRINTU8 + push af + call __PRINT_MINUS + pop af + neg +__PRINTU8: + PROC + LOCAL __PRINTU_LOOP + ld b, 0 ; Counter +__PRINTU_LOOP: + or a + jp z, __PRINTU_START + push bc + ld h, 10 + call __DIVU8_FAST ; Divides by 10. D'E'H'L' contains modulo (L' since < 10) + pop bc + ld a, l + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + ld a, h + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 17 "arch/zxnext/print_i8.bas" + END diff --git a/tests/functional/arch/zxnext/print_i8.bas b/tests/functional/arch/zxnext/print_i8.bas new file mode 100644 index 000000000..68878a0c4 --- /dev/null +++ b/tests/functional/arch/zxnext/print_i8.bas @@ -0,0 +1,2 @@ +DIM a as Byte +PRINT a; diff --git a/tests/functional/arch/zxnext/print_u16.asm b/tests/functional/arch/zxnext/print_u16.asm new file mode 100644 index 000000000..47869ad43 --- /dev/null +++ b/tests/functional/arch/zxnext/print_u16.asm @@ -0,0 +1,1099 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld hl, (_a) + call .core.__PRINTU16 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 16 "arch/zxnext/print_u16.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printu16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + ; 16 bit division and modulo functions + ; for both signed and unsigned values +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg16.asm" + ; Negates HL value (16 bit) + push namespace core +__ABS16: + bit 7, h + ret z +__NEGHL: + ld a, l ; HL = -HL + cpl + ld l, a + ld a, h + cpl + ld h, a + inc hl + ret + pop namespace +#line 5 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div16.asm" + push namespace core +__DIVU16: ; 16 bit unsigned division + ; HL = Dividend, Stack Top = Divisor + ; -- OBSOLETE ; Now uses FASTCALL convention + ; ex de, hl + ; pop hl ; Return address + ; ex (sp), hl ; CALLEE Convention +__DIVU16_FAST: + ld a, h + ld c, l + ld hl, 0 + ld b, 16 +__DIV16LOOP: + sll c + rla + adc hl,hl + sbc hl,de + jr nc, __DIV16NOADD + add hl,de + dec c +__DIV16NOADD: + djnz __DIV16LOOP + ex de, hl + ld h, a + ld l, c + ret ; HL = quotient, DE = Mudulus +__MODU16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVU16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret +__DIVI16: ; 16 bit signed division + ; --- The following is OBSOLETE --- + ; ex de, hl + ; pop hl + ; ex (sp), hl ; CALLEE Convention +__DIVI16_FAST: + ld a, d + xor h + ex af, af' ; BIT 7 of a contains result + bit 7, d ; DE is negative? + jr z, __DIVI16A + ld a, e ; DE = -DE + cpl + ld e, a + ld a, d + cpl + ld d, a + inc de +__DIVI16A: + bit 7, h ; HL is negative? + call nz, __NEGHL +__DIVI16B: + call __DIVU16_FAST + ex af, af' + or a + ret p ; return if positive + jp __NEGHL +__MODI16: ; 16 bit modulus + ; HL = Dividend, Stack Top = Divisor + ;ex de, hl + ;pop hl + ;ex (sp), hl ; CALLEE Convention + call __DIVI16_FAST + ex de, hl ; hl = reminder (modulus) + ; de = quotient + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi16.asm" + push namespace core +__PRINTI16: ; Prints a 16bits signed in HL + ; Converts 16 to 32 bits + PROC + LOCAL __PRINTU_LOOP + ld a, h + or a + jp p, __PRINTU16 + call __PRINT_MINUS + call __NEGHL +__PRINTU16: + ld b, 0 +__PRINTU_LOOP: + ld a, h + or l + jp z, __PRINTU_START + push bc + ld de, 10 + call __DIVU16_FAST ; Divides by DE. DE = MODULUS at exit. Since < 256, E = Modulus + pop bc + ld a, e + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printu16.asm" +#line 17 "arch/zxnext/print_u16.bas" + END diff --git a/tests/functional/arch/zxnext/print_u16.bas b/tests/functional/arch/zxnext/print_u16.bas new file mode 100644 index 000000000..a98edf98d --- /dev/null +++ b/tests/functional/arch/zxnext/print_u16.bas @@ -0,0 +1,2 @@ +DIM a as UInteger +PRINT a; diff --git a/tests/functional/arch/zxnext/print_u32.asm b/tests/functional/arch/zxnext/print_u32.asm new file mode 100644 index 000000000..81ff53420 --- /dev/null +++ b/tests/functional/arch/zxnext/print_u32.asm @@ -0,0 +1,1153 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld hl, (_a) + ld de, (_a + 2) + call .core.__PRINTU32 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 17 "arch/zxnext/print_u32.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printu32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div32.asm" + ; --------------------------------------------------------- + push namespace core +__DIVU32: ; 32 bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor + ; OPERANDS P = Dividend, Q = Divisor => OPERATION => P / Q + ; + ; Changes A, BC DE HL B'C' D'E' H'L' + ; --------------------------------------------------------- + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVU32START: ; Performs D'E'H'L' / HLDE + ; Now switch to DIVIDEND = B'C'BC / DIVISOR = D'E'DE (A / B) + push de ; push Lowpart(Q) + ex de, hl ; DE = HL + ld hl, 0 + exx + ld b, h + ld c, l + pop hl + push de + ex de, hl + ld hl, 0 ; H'L'HL = 0 + exx + pop bc ; Pop HightPart(B) => B = B'C'BC + exx + ld a, 32 ; Loop count +__DIV32LOOP: + sll c ; B'C'BC << 1 ; Output most left bit to carry + rl b + exx + rl c + rl b + exx + adc hl, hl + exx + adc hl, hl + exx + sbc hl,de + exx + sbc hl,de + exx + jp nc, __DIV32NOADD ; use JP inside a loop for being faster + add hl, de + exx + adc hl, de + exx + dec bc +__DIV32NOADD: + dec a + jp nz, __DIV32LOOP ; use JP inside a loop for being faster + ; At this point, quotient is stored in B'C'BC and the reminder in H'L'HL + push hl + exx + pop de + ex de, hl ; D'E'H'L' = 32 bits modulus + push bc + exx + pop de ; DE = B'C' + ld h, b + ld l, c ; DEHL = quotient D'E'H'L' = Modulus + ret ; DEHL = quotient, D'E'H'L' = Modulus +__MODU32: ; 32 bit modulus for 32bit unsigned division + ; DEHL = Dividend, Stack Top = Divisor (DE, HL) + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVU32START ; At return, modulus is at D'E'H'L' +__MODU32START: + exx + push de + push hl + exx + pop hl + pop de + ret +__DIVI32: ; 32 bit signed division + ; DEHL = Dividend, Stack Top = Divisor + ; A = Dividend, B = Divisor => A / B + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend +__DIVI32START: + exx + ld a, d ; Save sign + ex af, af' + bit 7, d ; Negative? + call nz, __NEG32 ; Negates DEHL + exx ; Now works with H'L'D'E' + ex af, af' + xor h + ex af, af' ; Stores sign of the result for later + bit 7, h ; Negative? + ex de, hl ; HLDE = DEHL + call nz, __NEG32 + ex de, hl + call __DIVU32START + ex af, af' ; Recovers sign + and 128 ; positive? + ret z + jp __NEG32 ; Negates DEHL and returns from there +__MODI32: ; 32bits signed division modulus + exx + pop hl ; return address + pop de ; low part + ex (sp), hl ; CALLEE Convention ; H'L'D'E' => Dividend + call __DIVI32START + jp __MODU32START + pop namespace +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/printi32.asm" + push namespace core +__PRINTI32: + ld a, d + or a + jp p, __PRINTU32 + call __PRINT_MINUS + call __NEG32 +__PRINTU32: + PROC + LOCAL __PRINTU_LOOP + ld b, 0 ; Counter +__PRINTU_LOOP: + ld a, h + or l + or d + or e + jp z, __PRINTU_START + push bc + ld bc, 0 + push bc + ld bc, 10 + push bc ; Push 00 0A (10 Dec) into the stack = divisor + call __DIVU32 ; Divides by 32. D'E'H'L' contains modulo (L' since < 10) + pop bc + exx + ld a, l + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + exx + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printu32.asm" +#line 18 "arch/zxnext/print_u32.bas" + END diff --git a/tests/functional/arch/zxnext/print_u32.bas b/tests/functional/arch/zxnext/print_u32.bas new file mode 100644 index 000000000..57b6efbf2 --- /dev/null +++ b/tests/functional/arch/zxnext/print_u32.bas @@ -0,0 +1,2 @@ +DIM a as ULong +PRINT a; diff --git a/tests/functional/arch/zxnext/print_u8.asm b/tests/functional/arch/zxnext/print_u8.asm new file mode 100644 index 000000000..1d0809d31 --- /dev/null +++ b/tests/functional/arch/zxnext/print_u8.asm @@ -0,0 +1,1073 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_a) + call .core.__PRINTU8 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +; vim:ts=4:sw=4:et: + ; PRINT command routine + ; Does not print attribute. Use PRINT_STR or PRINT_NUM for that +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TV_FLAG EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/in_screen.asm" + push namespace core +__IN_SCREEN: + ; Returns NO carry if current coords (D, E) + ; are OUT of the screen limits + PROC + LOCAL __IN_SCREEN_ERR + ld hl, SCR_SIZE + ld a, e + cp l + jr nc, __IN_SCREEN_ERR ; Do nothing and return if out of range + ld a, d + cp h + ret c ; Return if carry (OK) +__IN_SCREEN_ERR: +__OUT_OF_SCREEN_ERR: + ; Jumps here if out of screen + ld a, ERROR_OutOfScreen + jp __STOP ; Saves error code and exits + ENDP + pop namespace +#line 7 "/zxbasic/src/lib/arch/zxnext/runtime/attr.asm" + push namespace core +__ATTR_ADDR: + ; calc start address in DE (as (32 * d) + e) + ; Contributed by Santiago Romero at http://www.speccy.org + ld h, 0 ; 7 T-States + ld a, d ; 4 T-States + ld d, h + add a, a ; a * 2 ; 4 T-States + add a, a ; a * 4 ; 4 T-States + ld l, a ; HL = A * 4 ; 4 T-States + add hl, hl ; HL = A * 8 ; 15 T-States + add hl, hl ; HL = A * 16 ; 15 T-States + add hl, hl ; HL = A * 32 ; 15 T-States + add hl, de + ld de, (SCREEN_ATTR_ADDR) ; Adds the screen address + add hl, de + ; Return current screen address in HL + ret + ; Sets the attribute at a given screen coordinate (D, E). + ; The attribute is taken from the ATTR_T memory variable + ; Used by PRINT routines +SET_ATTR: + ; Checks for valid coords + call __IN_SCREEN + ret nc + call __ATTR_ADDR +__SET_ATTR: + ; Internal __FASTCALL__ Entry used by printing routines + ; HL contains the address of the ATTR cell to set + PROC +__SET_ATTR2: ; Sets attr from ATTR_T to (HL) which points to the scr address + ld de, (ATTR_T) ; E = ATTR_T, D = MASK_T + ld a, d + and (hl) + ld c, a ; C = current screen color, masked + ld a, d + cpl ; Negate mask + and e ; Mask current attributes + or c ; Mix them + ld (hl), a ; Store result in screen + ret + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/sposn.asm" + ; Printing positioning library. + push namespace core + ; Loads into DE current ROW, COL print position from S_POSN mem var. +__LOAD_S_POSN: + PROC + ld de, (S_POSN) + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + ret + ENDP + ; Saves ROW, COL from DE into S_POSN mem var. +__SAVE_S_POSN: + PROC + ld hl, SCR_SIZE + or a + sbc hl, de + ld (S_POSN), hl ; saves it again +__SET_SCR_PTR: ;; Fast + push de + call __ATTR_ADDR + ld (DFCCL), hl + pop de + ld a, d + ld c, a ; Saves it for later + and 0F8h ; Masks 3 lower bit ; zy + ld d, a + ld a, c ; Recovers it + and 07h ; MOD 7 ; y1 + rrca + rrca + rrca + or e + ld e, a + ld hl, (SCREEN_ADDR) + add hl, de ; HL = Screen address + DE + ld (DFCC), hl + ret + ENDP + pop namespace +#line 6 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/table_jump.asm" + push namespace core +JUMP_HL_PLUS_2A: ; Does JP (HL + A*2) Modifies DE. Modifies A + add a, a +JUMP_HL_PLUS_A: ; Does JP (HL + A) Modifies DE + ld e, a + ld d, 0 +JUMP_HL_PLUS_DE: ; Does JP (HL + DE) + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl +CALL_HL: + jp (hl) + pop namespace +#line 8 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ink.asm" + ; Sets ink color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +INK: + PROC + LOCAL __SET_INK + LOCAL __SET_INK2 + ld de, ATTR_P +__SET_INK: + cp 8 + jr nz, __SET_INK2 + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + or 7 ; Set bits 0,1,2 to enable transparency + ld (de), a + ret +__SET_INK2: + ; Another entry. This will set the ink color at location pointer by DE + and 7 ; # Gets color mod 8 + ld b, a ; Saves the color + ld a, (de) + and 0F8h ; Clears previous value + or b + ld (de), a + inc de ; Points DE to MASK_T or MASK_P + ld a, (de) + and 0F8h ; Reset bits 0,1,2 sign to disable transparency + ld (de), a ; Store new attr + ret + ; Sets the INK color passed in A register in the ATTR_T variable +INK_TMP: + ld de, ATTR_T + jp __SET_INK + ENDP + pop namespace +#line 9 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/paper.asm" + ; Sets paper color in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +PAPER: + PROC + LOCAL __SET_PAPER + LOCAL __SET_PAPER2 + ld de, ATTR_P +__SET_PAPER: + cp 8 + jr nz, __SET_PAPER2 + inc de + ld a, (de) + or 038h + ld (de), a + ret + ; Another entry. This will set the paper color at location pointer by DE +__SET_PAPER2: + and 7 ; # Remove + rlca + rlca + rlca ; a *= 8 + ld b, a ; Saves the color + ld a, (de) + and 0C7h ; Clears previous value + or b + ld (de), a + inc de ; Points to MASK_T or MASK_P accordingly + ld a, (de) + and 0C7h ; Resets bits 3,4,5 + ld (de), a + ret + ; Sets the PAPER color passed in A register in the ATTR_T variable +PAPER_TMP: + ld de, ATTR_T + jp __SET_PAPER + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/flash.asm" + ; Sets flash flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +FLASH: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_FLASH: + ; Another entry. This will set the flash flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x80 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 07Fh ; Clears previous value + or b + ld (hl), a + inc hl + res 7, (hl) ;Reset bit 7 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 7, (hl) ;Set bit 7 to enable transparency + ret + ; Sets the FLASH flag passed in A register in the ATTR_T variable +FLASH_TMP: + ld hl, ATTR_T + jr __SET_FLASH + ENDP + pop namespace +#line 11 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bright.asm" + ; Sets bright flag in ATTR_P permanently +; Parameter: Paper color in A register + push namespace core +BRIGHT: + ld hl, ATTR_P + PROC + LOCAL IS_TR + LOCAL IS_ZERO +__SET_BRIGHT: + ; Another entry. This will set the bright flag at location pointer by DE + cp 8 + jr z, IS_TR + ; # Convert to 0/1 + or a + jr z, IS_ZERO + ld a, 0x40 +IS_ZERO: + ld b, a ; Saves the color + ld a, (hl) + and 0BFh ; Clears previous value + or b + ld (hl), a + inc hl + res 6, (hl) ;Reset bit 6 to disable transparency + ret +IS_TR: ; transparent + inc hl ; Points DE to MASK_T or MASK_P + set 6, (hl) ;Set bit 6 to enable transparency + ret + ; Sets the BRIGHT flag passed in A register in the ATTR_T variable +BRIGHT_TMP: + ld hl, ATTR_T + jr __SET_BRIGHT + ENDP + pop namespace +#line 12 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/over.asm" + ; Sets OVER flag in P_FLAG permanently +; Parameter: OVER flag in bit 0 of A register + push namespace core +OVER: + PROC + ld c, a ; saves it for later + and 2 + ld hl, FLAGS2 + res 1, (HL) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 ; # Convert to 0/1 + add a, a; # Shift left 1 bit for permanent + ld hl, P_FLAG + res 1, (hl) + or (hl) + ld (hl), a + ret + ; Sets OVER flag in P_FLAG temporarily +OVER_TMP: + ld c, a ; saves it for later + and 2 ; gets bit 1; clears carry + rra + ld hl, FLAGS2 + res 0, (hl) + or (hl) + ld (hl), a + ld a, c ; Recovers previous value + and 1 + ld hl, P_FLAG + res 0, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/inverse.asm" + ; Sets INVERSE flag in P_FLAG permanently +; Parameter: INVERSE flag in bit 0 of A register + push namespace core +INVERSE: + PROC + and 1 ; # Convert to 0/1 + add a, a; # Shift left 3 bits for permanent + add a, a + add a, a + ld hl, P_FLAG + res 3, (hl) + or (hl) + ld (hl), a + ret + ; Sets INVERSE flag in P_FLAG temporarily +INVERSE_TMP: + and 1 + add a, a + add a, a; # Shift left 2 bits for temporary + ld hl, P_FLAG + res 2, (hl) + or (hl) + ld (hl), a + jp __SET_ATTR_MODE + ENDP + pop namespace +#line 14 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/bold.asm" + ; Sets BOLD flag in P_FLAG permanently +; Parameter: BOLD flag in bit 0 of A register + push namespace core +BOLD: + PROC + and 1 + rlca + rlca + rlca + ld hl, FLAGS2 + res 3, (HL) + or (hl) + ld (hl), a + ret + ; Sets BOLD flag in P_FLAG temporarily +BOLD_TMP: + and 1 + rlca + rlca + ld hl, FLAGS2 + res 2, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 15 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/italic.asm" + ; Sets ITALIC flag in P_FLAG permanently +; Parameter: ITALIC flag in bit 0 of A register + push namespace core +ITALIC: + PROC + and 1 + rrca + rrca + rrca + ld hl, FLAGS2 + res 5, (HL) + or (hl) + ld (hl), a + ret + ; Sets ITALIC flag in P_FLAG temporarily +ITALIC_TMP: + and 1 + rrca + rrca + rrca + rrca + ld hl, FLAGS2 + res 4, (hl) + or (hl) + ld (hl), a + ret + ENDP + pop namespace +#line 16 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ; Putting a comment starting with @INIT
+ ; will make the compiler to add a CALL to
+ ; It is useful for initialization routines. + push namespace core +__PRINT_INIT: ; To be called before program starts (initializes library) + PROC + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + ;; Clears ATTR2 flags (OVER 2, etc) + xor a + ld (FLAGS2), a + ld hl, TV_FLAG + res 0, (hl) + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 94 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 141 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 146 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + ld hl, (DFCC) + push hl + ld b, 8 ; 8 bytes per char +__PRCHAR: + ld a, (de) ; DE *must* be source, and HL destiny +PRINT_MODE: ; Which operation is used to write on the screen + ; Set it with: + ; LD A, + ; LD (PRINT_MODE), A + ; + ; Available operations: + ; NORMAL : 0h --> NOP ; OVER 0 + ; XOR : AEh --> XOR (HL) ; OVER 1 + ; OR : B6h --> OR (HL) ; PUTSPRITE + ; AND : A6h --> AND (HL) ; PUTMASK + nop ; Set to one of the values above +INVERSE_MODE: ; 00 -> NOP -> INVERSE 0 + nop ; 2F -> CPL -> INVERSE 1 + ld (hl), a + inc de + inc h ; Next line + djnz __PRCHAR + pop hl + inc hl + ld (DFCC), hl + ld hl, (DFCCL) ; current ATTR Pos + inc hl + ld (DFCCL), hl + dec hl + call __SET_ATTR + exx + ret + ; ------------- SPECIAL CHARS (< 32) ----------------- +__PRINT_SPECIAL: ; Jumps here if it is a special char + ld hl, __PRINT_TABLE + jp JUMP_HL_PLUS_2A +PRINT_EOL: ; Called WHENEVER there is no ";" at end of PRINT sentence + exx +__PRINT_0Dh: ; Called WHEN printing CHR$(13) + ld hl, (S_POSN) + dec l + jr nz, 1f + dec h + jr nz, 1f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 211 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 355 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 365 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __BOLD +__BOLD: + push hl + ld hl, MEM0 + ld b, 8 +1: + ld a, (de) + ld c, a + rlca + or c + ld (hl), a + inc hl + inc de + djnz 1b + pop hl + ld de, MEM0 + ret +#line 386 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __ITALIC +__ITALIC: + push hl + ld hl, MEM0 + ex de, hl + ld bc, 8 + ldir + ld hl, MEM0 + srl (hl) + inc hl + srl (hl) + inc hl + srl (hl) + inc hl + inc hl + inc hl + sla (hl) + inc hl + sla (hl) + inc hl + sla (hl) + pop hl + ld de, MEM0 + ret +#line 414 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __SCROLL_SCR +#line 488 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 490 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +#line 491 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +PRINT_COMMA: + call __LOAD_S_POSN + ld a, e + and 16 + add a, 16 +PRINT_TAB: + ; Tabulates the number of spaces in A register + ; If the current cursor position is already A, does nothing + PROC + LOCAL LOOP + call __LOAD_S_POSN ; e = current row + sub e + and 31 + ret z + ld b, a +LOOP: + ld a, ' ' + call __PRINTCHAR + djnz LOOP + ret + ENDP +PRINT_AT: ; Changes cursor to ROW, COL + ; COL in A register + ; ROW in stack + pop hl ; Ret address + ex (sp), hl ; callee H = ROW + ld l, a + ex de, hl + call __IN_SCREEN + ret nc ; Return if out of screen + jp __SAVE_S_POSN + LOCAL __PRINT_COM + LOCAL __PRINT_AT1 + LOCAL __PRINT_AT2 + LOCAL __PRINT_BOLD + LOCAL __PRINT_ITA + LOCAL __PRINT_INK + LOCAL __PRINT_PAP + LOCAL __PRINT_SET_STATE + LOCAL __PRINT_TABLE + LOCAL __PRINT_TAB, __PRINT_TAB1, __PRINT_TAB2 + LOCAL __PRINT_ITA2 +#line 547 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 553 "/zxbasic/src/lib/arch/zxnext/runtime/print.asm" +__PRINT_TABLE: ; Jump table for 0 .. 22 codes + DW __PRINT_NOP ; 0 + DW __PRINT_NOP ; 1 + DW __PRINT_NOP ; 2 + DW __PRINT_NOP ; 3 + DW __PRINT_NOP ; 4 + DW __PRINT_NOP ; 5 + DW __PRINT_COM ; 6 COMMA + DW __PRINT_NOP ; 7 + DW __PRINT_DEL ; 8 DEL + DW __PRINT_NOP ; 9 + DW __PRINT_NOP ; 10 + DW __PRINT_NOP ; 11 + DW __PRINT_NOP ; 12 + DW __PRINT_0Dh ; 13 + DW __PRINT_BOLD ; 14 + DW __PRINT_ITA ; 15 + DW __PRINT_INK ; 16 + DW __PRINT_PAP ; 17 + DW __PRINT_FLA ; 18 + DW __PRINT_BRI ; 19 + DW __PRINT_INV ; 20 + DW __PRINT_OVR ; 21 + DW __PRINT_AT ; 22 AT + DW __PRINT_TAB ; 23 TAB + ENDP + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" + push namespace core +COPY_ATTR: + ; Just copies current permanent attribs into temporal attribs + ; and sets print mode + PROC + LOCAL INVERSE1 + LOCAL __REFRESH_TMP + INVERSE1 EQU 02Fh + ld hl, (ATTR_P) + ld (ATTR_T), hl + ld hl, FLAGS2 + call __REFRESH_TMP + ld hl, P_FLAG + call __REFRESH_TMP +__SET_ATTR_MODE: ; Another entry to set print modes. A contains (P_FLAG) + LOCAL TABLE + LOCAL CONT2 + rra ; Over bit to carry + ld a, (FLAGS2) + rla ; Over bit in bit 1, Over2 bit in bit 2 + and 3 ; Only bit 0 and 1 (OVER flag) + ld c, a + ld b, 0 + ld hl, TABLE + add hl, bc + ld a, (hl) + ld (PRINT_MODE), a + ld hl, (P_FLAG) + xor a ; NOP -> INVERSE0 + bit 2, l + jr z, CONT2 + ld a, INVERSE1 ; CPL -> INVERSE1 +CONT2: + ld (INVERSE_MODE), a + ret +TABLE: + nop ; NORMAL MODE + xor (hl) ; OVER 1 MODE + and (hl) ; OVER 2 MODE + or (hl) ; OVER 3 MODE +#line 67 "/zxbasic/src/lib/arch/zxnext/runtime/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 16 "arch/zxnext/print_u8.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printu8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/printnum.asm" + push namespace core +__PRINTU_START: + PROC + LOCAL __PRINTU_CONT + ld a, b + or a + jp nz, __PRINTU_CONT + ld a, '0' + jp __PRINT_DIGIT +__PRINTU_CONT: + pop af + push bc + call __PRINT_DIGIT + pop bc + djnz __PRINTU_CONT + ret + ENDP +__PRINT_MINUS: ; PRINT the MINUS (-) sign. CALLER must preserve registers + ld a, '-' + jp __PRINT_DIGIT + __PRINT_DIGIT EQU __PRINTCHAR ; PRINTS the char in A register, and puts its attrs + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/div8.asm" + ; -------------------------------- + push namespace core +__DIVU8: ; 8 bit unsigned integer division + ; Divides (Top of stack, High Byte) / A + pop hl ; -------------------------------- + ex (sp), hl ; CALLEE +__DIVU8_FAST: ; Does A / H + ld l, h + ld h, a ; At this point do H / L + ld b, 8 + xor a ; A = 0, Carry Flag = 0 +__DIV8LOOP: + sla h + rla + cp l + jr c, __DIV8NOSUB + sub l + inc h +__DIV8NOSUB: + djnz __DIV8LOOP + ld l, a ; save remainder + ld a, h ; + ret ; a = Quotient, + ; -------------------------------- +__DIVI8: ; 8 bit signed integer division Divides (Top of stack) / A + pop hl ; -------------------------------- + ex (sp), hl +__DIVI8_FAST: + ld e, a ; store operands for later + ld c, h + or a ; negative? + jp p, __DIV8A + neg ; Make it positive +__DIV8A: + ex af, af' + ld a, h + or a + jp p, __DIV8B + neg + ld h, a ; make it positive +__DIV8B: + ex af, af' + call __DIVU8_FAST + ld a, c + xor l ; bit 7 of A = 1 if result is negative + ld a, h ; Quotient + ret p ; return if positive + neg + ret +__MODU8: ; 8 bit module. REturns A mod (Top of stack) (unsigned operands) + pop hl + ex (sp), hl ; CALLEE +__MODU8_FAST: ; __FASTCALL__ entry + call __DIVU8_FAST + ld a, l ; Remainder + ret ; a = Modulus +__MODI8: ; 8 bit module. REturns A mod (Top of stack) (For singed operands) + pop hl + ex (sp), hl ; CALLEE +__MODI8_FAST: ; __FASTCALL__ entry + call __DIVI8_FAST + ld a, l ; remainder + ret ; a = Modulus + pop namespace +#line 3 "/zxbasic/src/lib/arch/zxnext/runtime/printi8.asm" + push namespace core +__PRINTI8: ; Prints an 8 bits number in Accumulator (A) + ; Converts 8 to 32 bits + or a + jp p, __PRINTU8 + push af + call __PRINT_MINUS + pop af + neg +__PRINTU8: + PROC + LOCAL __PRINTU_LOOP + ld b, 0 ; Counter +__PRINTU_LOOP: + or a + jp z, __PRINTU_START + push bc + ld h, 10 + call __DIVU8_FAST ; Divides by 10. D'E'H'L' contains modulo (L' since < 10) + pop bc + ld a, l + or '0' ; Stores ASCII digit (must be print in reversed order) + push af + ld a, h + inc b + jp __PRINTU_LOOP ; Uses JP in loops + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/printu8.asm" +#line 17 "arch/zxnext/print_u8.bas" + END diff --git a/tests/functional/arch/zxnext/print_u8.bas b/tests/functional/arch/zxnext/print_u8.bas new file mode 100644 index 000000000..1f2bf2f99 --- /dev/null +++ b/tests/functional/arch/zxnext/print_u8.bas @@ -0,0 +1,2 @@ +DIM a as Ubyte +PRINT a; diff --git a/tests/functional/arch/zxnext/sub16.asm b/tests/functional/arch/zxnext/sub16.asm new file mode 100644 index 000000000..f6e15c817 --- /dev/null +++ b/tests/functional/arch/zxnext/sub16.asm @@ -0,0 +1,55 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld (_b), hl + ld hl, (_a) + dec hl + ld (_b), hl + ld hl, 0 + ld de, (_a) + or a + sbc hl, de + ld (_b), hl + ld hl, 1 + ld de, (_a) + or a + sbc hl, de + ld (_b), hl + ld hl, (_a) + ld de, (_a) + or a + sbc hl, de + ld (_b), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/sub16.bas b/tests/functional/arch/zxnext/sub16.bas new file mode 100644 index 000000000..547ef96f7 --- /dev/null +++ b/tests/functional/arch/zxnext/sub16.bas @@ -0,0 +1,10 @@ +' TEST for SUB16 bits + +DIM a as UInteger +DIM b as UInteger + +b = a - 0 +b = a - 1 +b = 0 - a +b = 1 - a +b = a - a diff --git a/tests/functional/arch/zxnext/sub16a.asm b/tests/functional/arch/zxnext/sub16a.asm new file mode 100644 index 000000000..64ade5ed0 --- /dev/null +++ b/tests/functional/arch/zxnext/sub16a.asm @@ -0,0 +1,41 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a) + or a + sbc hl, de + ld de, (_a) + or a + sbc hl, de + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/sub16a.bas b/tests/functional/arch/zxnext/sub16a.bas new file mode 100644 index 000000000..5958f41ab --- /dev/null +++ b/tests/functional/arch/zxnext/sub16a.bas @@ -0,0 +1,4 @@ + +REM another SUB16 tests +DIM a As Uinteger +a = a - a - a diff --git a/tests/functional/arch/zxnext/sub16b.asm b/tests/functional/arch/zxnext/sub16b.asm new file mode 100644 index 000000000..facef16bd --- /dev/null +++ b/tests/functional/arch/zxnext/sub16b.asm @@ -0,0 +1,49 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_a) + ld de, (_a) + or a + sbc hl, de + push hl + ld hl, (_a) + dec hl + dec hl + ld de, (_a) + or a + sbc hl, de + ex de, hl + pop hl + or a + sbc hl, de + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/sub16b.bas b/tests/functional/arch/zxnext/sub16b.bas new file mode 100644 index 000000000..0fcad1bea --- /dev/null +++ b/tests/functional/arch/zxnext/sub16b.bas @@ -0,0 +1,4 @@ + +REM another SUB16 test +DIM a As UInteger +a = a - a - (a - 2 - a) diff --git a/tests/functional/arch/zxnext/sub8.asm b/tests/functional/arch/zxnext/sub8.asm new file mode 100644 index 000000000..7489771ff --- /dev/null +++ b/tests/functional/arch/zxnext/sub8.asm @@ -0,0 +1,51 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +_b: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld (_b), a + ld a, (_a) + dec a + ld (_b), a + ld a, (_a) + neg + ld (_b), a + ld a, 1 + ld hl, (_a - 1) + sub h + ld (_b), a + ld a, (_a) + ld hl, (_a - 1) + sub h + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/sub8.bas b/tests/functional/arch/zxnext/sub8.bas new file mode 100644 index 000000000..3a1c98f00 --- /dev/null +++ b/tests/functional/arch/zxnext/sub8.bas @@ -0,0 +1,10 @@ +' TEST for Booleand AND 8 bits + +DIM a as Ubyte +DIM b as Ubyte + +b = a - 0 +b = a - 1 +b = 0 - a +b = 1 - a +b = a - a diff --git a/tests/functional/arch/zxnext/sub8a.asm b/tests/functional/arch/zxnext/sub8a.asm new file mode 100644 index 000000000..f01492004 --- /dev/null +++ b/tests/functional/arch/zxnext/sub8a.asm @@ -0,0 +1,39 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + sub h + ld hl, (_a - 1) + sub h + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/sub8a.bas b/tests/functional/arch/zxnext/sub8a.bas new file mode 100644 index 000000000..f0e9cc8bb --- /dev/null +++ b/tests/functional/arch/zxnext/sub8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a - a - a diff --git a/tests/functional/arch/zxnext/sub8b.asm b/tests/functional/arch/zxnext/sub8b.asm new file mode 100644 index 000000000..5f934f4f9 --- /dev/null +++ b/tests/functional/arch/zxnext/sub8b.asm @@ -0,0 +1,45 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_a) + ld hl, (_a - 1) + sub h + push af + ld a, (_a) + sub 2 + ld hl, (_a - 1) + sub h + ld h, a + pop af + sub h + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zxnext/sub8b.bas b/tests/functional/arch/zxnext/sub8b.bas new file mode 100644 index 000000000..895fb267d --- /dev/null +++ b/tests/functional/arch/zxnext/sub8b.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a - a - (a - 2 - a) diff --git a/tests/functional/arch/zxnext/subf00.asm b/tests/functional/arch/zxnext/subf00.asm new file mode 100644 index 000000000..3d3b8575f --- /dev/null +++ b/tests/functional/arch/zxnext/subf00.asm @@ -0,0 +1,168 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 80h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_b: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _b + 4 + call .core.__FP_PUSH_REV + ld a, 082h + ld de, 00000h + ld bc, 00000h + call .core.__SUBF + ld hl, _b + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__SUBF: ; Subtraction + call __FPSTACK_PUSH2 ; ENTERS B, A + ; ------------- ROM SUB + rst 28h + defb 01h ; EXCHANGE + defb 03h ; SUB + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 21 "arch/zxnext/subf00.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/pushf.asm" + ; Routine to push Float pointed by HL + ; Into the stack. Notice that the hl points to the last + ; byte of the FP number. + ; Uses H'L' B'C' and D'E' to preserve ABCDEHL registers + push namespace core +__FP_PUSH_REV: + push hl + exx + pop hl + pop bc ; Return Address + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + dec hl + ld e, (hl) + dec hl + push de + ld d, (hl) + push de + push bc ; Return Address + exx + ret + pop namespace +#line 22 "arch/zxnext/subf00.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 23 "arch/zxnext/subf00.bas" + END diff --git a/tests/functional/arch/zxnext/subf00.bas b/tests/functional/arch/zxnext/subf00.bas new file mode 100644 index 000000000..f76db1231 --- /dev/null +++ b/tests/functional/arch/zxnext/subf00.bas @@ -0,0 +1,4 @@ +DIM a as FLOAT = 0.5 +DIM b as FLOAT = 1 + +b = b - 2 diff --git a/tests/functional/arch/zxnext/subf01.asm b/tests/functional/arch/zxnext/subf01.asm new file mode 100644 index 000000000..7f3d2b01e --- /dev/null +++ b/tests/functional/arch/zxnext/subf01.asm @@ -0,0 +1,144 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_a: + DEFB 80h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_b: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld de, (_b + 1) + ld bc, (_b + 3) + ld hl, 00000h + push hl + ld hl, 00000h + push hl + ld h, 082h + push hl + call .core.__SUBF + ld hl, _b + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + push namespace core + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/arith/subf.asm" + ; ------------------------------------------------------------- + ; Floating point library using the FP ROM Calculator (ZX 48K) + ; All of them uses A EDCB registers as 1st paramter. + ; For binary operators, the 2n operator must be pushed into the + ; stack, in the order A DE BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__SUBF: ; Subtraction + call __FPSTACK_PUSH2 ; ENTERS B, A + ; ------------- ROM SUB + rst 28h + defb 01h ; EXCHANGE + defb 03h ; SUB + defb 38h; ; END CALC + jp __FPSTACK_POP + pop namespace +#line 25 "arch/zxnext/subf01.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 26 "arch/zxnext/subf01.bas" + END diff --git a/tests/functional/arch/zxnext/subf01.bas b/tests/functional/arch/zxnext/subf01.bas new file mode 100644 index 000000000..49f56f359 --- /dev/null +++ b/tests/functional/arch/zxnext/subf01.bas @@ -0,0 +1,4 @@ +DIM a as FLOAT = 0.5 +DIM b as FLOAT = 1 + +b = 2 - b diff --git a/tests/functional/arch/zxnext/subf16c.asm b/tests/functional/arch/zxnext/subf16c.asm new file mode 100644 index 000000000..451e6ffd4 --- /dev/null +++ b/tests/functional/arch/zxnext/subf16c.asm @@ -0,0 +1,116 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__SWAP32 + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 41 "arch/zxnext/subf16c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 42 "arch/zxnext/subf16c.bas" + END diff --git a/tests/functional/arch/zxnext/subf16c.bas b/tests/functional/arch/zxnext/subf16c.bas new file mode 100644 index 000000000..9e24837ad --- /dev/null +++ b/tests/functional/arch/zxnext/subf16c.bas @@ -0,0 +1,7 @@ +dim level as fixed = 1 +dim le as fixed = 0 +dim l as fixed + +l = le - (level + 0) +l = le - level +l = (le + 0) - level diff --git a/tests/functional/arch/zxnext/subi32c.asm b/tests/functional/arch/zxnext/subi32c.asm new file mode 100644 index 000000000..7106b0129 --- /dev/null +++ b/tests/functional/arch/zxnext/subi32c.asm @@ -0,0 +1,125 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_level: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_level + 2) + push de + push hl + ld de, (_le + 2) + ld hl, (_le) + call .core.__SWAP32 + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le + 2) + push hl + ld hl, (_le) + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_le) + ld de, (_le + 2) + push de + push hl + ld hl, (_level) + ld de, (_level + 2) + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, (_level) + ld de, (_level + 2) + ld bc, 0 + push bc + ld bc, 1 + push bc + call .core.__SUB32 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/arith/sub32.asm" + ; SUB32 + ; Perform TOP of the stack - DEHL + ; Pops operand out of the stack (CALLEE) + ; and returns result in DEHL. Carry an Z are set correctly + push namespace core +__SUB32: + exx + pop bc ; saves return address in BC' + exx + or a ; clears carry flag + ld b, h ; Operands come reversed => BC <- HL, HL = HL - BC + ld c, l + pop hl + sbc hl, bc + ex de, hl + ld b, h ; High part (DE) now in HL. Repeat operation + ld c, l + pop hl + sbc hl, bc + ex de, hl ; DEHL now has de 32 bit result + exx + push bc ; puts return address back + exx + ret + pop namespace +#line 50 "arch/zxnext/subi32c.bas" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/swap32.asm" + ; Exchanges current DE HL with the + ; ones in the stack + push namespace core +__SWAP32: + pop bc ; Return address + ex (sp), hl + inc sp + inc sp + ex de, hl + ex (sp), hl + ex de, hl + dec sp + dec sp + push bc + ret + pop namespace +#line 51 "arch/zxnext/subi32c.bas" + END diff --git a/tests/functional/arch/zxnext/subi32c.bas b/tests/functional/arch/zxnext/subi32c.bas new file mode 100644 index 000000000..73566b4c7 --- /dev/null +++ b/tests/functional/arch/zxnext/subi32c.bas @@ -0,0 +1,8 @@ +dim level as long = 1 +dim le as long = 0 +dim l as long + +l = le - (level + 0) +l = le - level +l = (le + 0) - level +l = 1 - level