From 5b5ff22f4f81e9eaf4c296864d9ee556236f6785 Mon Sep 17 00:00:00 2001 From: Jose Rodriguez Date: Sat, 1 Aug 2026 08:26:09 +0200 Subject: [PATCH] test: add tests for zx81sd arch --- tests/functional/arch/zx81sd/add16.asm | 90 + tests/functional/arch/zx81sd/add16.bas | 10 + tests/functional/arch/zx81sd/add16a.asm | 81 + tests/functional/arch/zx81sd/add16a.bas | 4 + tests/functional/arch/zx81sd/add16b.asm | 88 + tests/functional/arch/zx81sd/add16b.bas | 4 + tests/functional/arch/zx81sd/add32.asm | 114 + tests/functional/arch/zx81sd/add32.bas | 10 + tests/functional/arch/zx81sd/add32a.asm | 90 + tests/functional/arch/zx81sd/add32a.bas | 4 + tests/functional/arch/zx81sd/add32b.asm | 105 + tests/functional/arch/zx81sd/add32b.bas | 3 + tests/functional/arch/zx81sd/add8.asm | 90 + tests/functional/arch/zx81sd/add8.bas | 10 + tests/functional/arch/zx81sd/add8a.asm | 81 + tests/functional/arch/zx81sd/add8a.bas | 4 + tests/functional/arch/zx81sd/add8b.asm | 87 + tests/functional/arch/zx81sd/add8b.bas | 4 + tests/functional/arch/zx81sd/addf.asm | 2428 +++++++ tests/functional/arch/zx81sd/addf.bas | 3 + tests/functional/arch/zx81sd/addf16.asm | 114 + tests/functional/arch/zx81sd/addf16.bas | 10 + tests/functional/arch/zx81sd/addf16a.asm | 90 + tests/functional/arch/zx81sd/addf16a.bas | 4 + tests/functional/arch/zx81sd/addf16b.asm | 105 + tests/functional/arch/zx81sd/addf16b.bas | 4 + tests/functional/arch/zx81sd/addstr.asm | 2941 ++++++++ tests/functional/arch/zx81sd/addstr.bas | 10 + tests/functional/arch/zx81sd/array00.asm | 113 + tests/functional/arch/zx81sd/array00.bas | 6 + tests/functional/arch/zx81sd/array01.asm | 95 + tests/functional/arch/zx81sd/array01.bas | 5 + tests/functional/arch/zx81sd/array02.asm | 97 + tests/functional/arch/zx81sd/array02.bas | 6 + tests/functional/arch/zx81sd/array03.asm | 2524 +++++++ tests/functional/arch/zx81sd/array03.bas | 6 + tests/functional/arch/zx81sd/array04.asm | 106 + tests/functional/arch/zx81sd/array04.bas | 5 + tests/functional/arch/zx81sd/array05.asm | 108 + tests/functional/arch/zx81sd/array05.bas | 6 + tests/functional/arch/zx81sd/array06.asm | 2535 +++++++ tests/functional/arch/zx81sd/array06.bas | 6 + tests/functional/arch/zx81sd/array07.asm | 3234 +++++++++ tests/functional/arch/zx81sd/array07.bas | 6 + tests/functional/arch/zx81sd/array08.asm | 3214 +++++++++ tests/functional/arch/zx81sd/array08.bas | 5 + tests/functional/arch/zx81sd/array09.asm | 3214 +++++++++ tests/functional/arch/zx81sd/array09.bas | 6 + tests/functional/arch/zx81sd/arraycopy0.asm | 118 + tests/functional/arch/zx81sd/arraycopy0.bas | 8 + tests/functional/arch/zx81sd/arraycopy1.asm | 2918 ++++++++ tests/functional/arch/zx81sd/arraycopy1.bas | 6 + tests/functional/arch/zx81sd/arraycopy2.asm | 2926 ++++++++ tests/functional/arch/zx81sd/arraycopy2.bas | 5 + tests/functional/arch/zx81sd/arraycopy3.asm | 2930 ++++++++ tests/functional/arch/zx81sd/arraycopy3.bas | 5 + tests/functional/arch/zx81sd/arraycopy4.asm | 3251 +++++++++ tests/functional/arch/zx81sd/arraycopy4.bas | 9 + tests/functional/arch/zx81sd/arraycopy5.asm | 3133 ++++++++ tests/functional/arch/zx81sd/arraycopy5.bas | 5 + tests/functional/arch/zx81sd/bound00.asm | 104 + tests/functional/arch/zx81sd/bound00.bas | 5 + tests/functional/arch/zx81sd/bound01.asm | 151 + tests/functional/arch/zx81sd/bound01.bas | 7 + tests/functional/arch/zx81sd/bound02.asm | 248 + tests/functional/arch/zx81sd/bound02.bas | 7 + tests/functional/arch/zx81sd/bound03.asm | 248 + tests/functional/arch/zx81sd/bound03.bas | 8 + tests/functional/arch/zx81sd/bound04.asm | 131 + tests/functional/arch/zx81sd/bound04.bas | 6 + .../arch/zx81sd/cast_const_to_int.asm | 268 + .../arch/zx81sd/cast_const_to_int.bas | 25 + .../arch/zx81sd/cast_f16_to_long.asm | 90 + .../arch/zx81sd/cast_f16_to_long.bas | 4 + .../arch/zx81sd/cast_f16_to_ulong.asm | 90 + .../arch/zx81sd/cast_f16_to_ulong.bas | 4 + tests/functional/arch/zx81sd/div32.asm | 2526 +++++++ tests/functional/arch/zx81sd/div32.bas | 3 + tests/functional/arch/zx81sd/divf00.asm | 2460 +++++++ tests/functional/arch/zx81sd/divf00.bas | 4 + tests/functional/arch/zx81sd/divf01.asm | 2436 +++++++ tests/functional/arch/zx81sd/divf01.bas | 4 + tests/functional/arch/zx81sd/divf16.asm | 354 + tests/functional/arch/zx81sd/divf16.bas | 13 + tests/functional/arch/zx81sd/divf16a.asm | 299 + tests/functional/arch/zx81sd/divf16a.bas | 4 + tests/functional/arch/zx81sd/divf16b.asm | 309 + tests/functional/arch/zx81sd/divf16b.bas | 4 + tests/functional/arch/zx81sd/divf16c.asm | 359 + tests/functional/arch/zx81sd/divf16c.bas | 9 + tests/functional/arch/zx81sd/divi16a.asm | 173 + tests/functional/arch/zx81sd/divi16a.bas | 4 + tests/functional/arch/zx81sd/divi16b.asm | 180 + tests/functional/arch/zx81sd/divi16b.bas | 4 + tests/functional/arch/zx81sd/divi32c.asm | 291 + tests/functional/arch/zx81sd/divi32c.bas | 9 + tests/functional/arch/zx81sd/divi8.asm | 169 + tests/functional/arch/zx81sd/divi8.bas | 13 + tests/functional/arch/zx81sd/divi8a.asm | 146 + tests/functional/arch/zx81sd/divi8a.bas | 4 + tests/functional/arch/zx81sd/divi8b.asm | 152 + tests/functional/arch/zx81sd/divi8b.bas | 4 + tests/functional/arch/zx81sd/divu16.asm | 197 + tests/functional/arch/zx81sd/divu16.bas | 13 + tests/functional/arch/zx81sd/divu16a.asm | 173 + tests/functional/arch/zx81sd/divu16a.bas | 4 + tests/functional/arch/zx81sd/divu16b.asm | 180 + tests/functional/arch/zx81sd/divu16b.bas | 4 + tests/functional/arch/zx81sd/divu32c.asm | 291 + tests/functional/arch/zx81sd/divu32c.bas | 9 + tests/functional/arch/zx81sd/divu8.asm | 169 + tests/functional/arch/zx81sd/divu8.bas | 13 + tests/functional/arch/zx81sd/divu8a.asm | 146 + tests/functional/arch/zx81sd/divu8a.bas | 4 + tests/functional/arch/zx81sd/divu8b.asm | 152 + tests/functional/arch/zx81sd/divu8b.bas | 4 + tests/functional/arch/zx81sd/equ16.asm | 94 + tests/functional/arch/zx81sd/equ16.bas | 5 + tests/functional/arch/zx81sd/equ32.asm | 120 + tests/functional/arch/zx81sd/equ32.bas | 5 + tests/functional/arch/zx81sd/equ8.asm | 80 + tests/functional/arch/zx81sd/equ8.bas | 5 + tests/functional/arch/zx81sd/equf.asm | 2611 +++++++ tests/functional/arch/zx81sd/equf.bas | 5 + tests/functional/arch/zx81sd/fact.asm | 252 + tests/functional/arch/zx81sd/fact.bas | 14 + tests/functional/arch/zx81sd/fp_pow.asm | 2418 +++++++ tests/functional/arch/zx81sd/fp_pow.bas | 3 + tests/functional/arch/zx81sd/gef.asm | 2644 +++++++ tests/functional/arch/zx81sd/gef.bas | 9 + tests/functional/arch/zx81sd/gef16.asm | 217 + tests/functional/arch/zx81sd/gef16.bas | 8 + tests/functional/arch/zx81sd/gei16.asm | 130 + tests/functional/arch/zx81sd/gei16.bas | 9 + tests/functional/arch/zx81sd/gei32.asm | 233 + tests/functional/arch/zx81sd/gei32.bas | 9 + tests/functional/arch/zx81sd/gei8.asm | 105 + tests/functional/arch/zx81sd/gei8.bas | 5 + tests/functional/arch/zx81sd/geu16.asm | 137 + tests/functional/arch/zx81sd/geu16.bas | 9 + tests/functional/arch/zx81sd/geu32.asm | 210 + tests/functional/arch/zx81sd/geu32.bas | 9 + tests/functional/arch/zx81sd/geu8.asm | 117 + tests/functional/arch/zx81sd/geu8.bas | 9 + tests/functional/arch/zx81sd/gtf.asm | 2631 +++++++ tests/functional/arch/zx81sd/gtf.bas | 8 + tests/functional/arch/zx81sd/gtf16.asm | 225 + tests/functional/arch/zx81sd/gtf16.bas | 8 + tests/functional/arch/zx81sd/gti16.asm | 152 + tests/functional/arch/zx81sd/gti16.bas | 9 + tests/functional/arch/zx81sd/gti32.asm | 241 + tests/functional/arch/zx81sd/gti32.bas | 9 + tests/functional/arch/zx81sd/gti8.asm | 107 + tests/functional/arch/zx81sd/gti8.bas | 5 + tests/functional/arch/zx81sd/gtu16.asm | 125 + tests/functional/arch/zx81sd/gtu16.bas | 9 + tests/functional/arch/zx81sd/gtu32.asm | 201 + tests/functional/arch/zx81sd/gtu32.bas | 9 + tests/functional/arch/zx81sd/gtu8.asm | 81 + tests/functional/arch/zx81sd/gtu8.bas | 5 + tests/functional/arch/zx81sd/inkey.asm | 3193 ++++++++ tests/functional/arch/zx81sd/inkey.bas | 1 + tests/functional/arch/zx81sd/lbound12.asm | 3149 ++++++++ tests/functional/arch/zx81sd/lbound12.bas | 21 + tests/functional/arch/zx81sd/lef.asm | 2644 +++++++ tests/functional/arch/zx81sd/lef.bas | 9 + tests/functional/arch/zx81sd/lei16.asm | 137 + tests/functional/arch/zx81sd/lei16.bas | 9 + tests/functional/arch/zx81sd/lei32.asm | 226 + tests/functional/arch/zx81sd/lei32.bas | 9 + tests/functional/arch/zx81sd/lei8.asm | 125 + tests/functional/arch/zx81sd/lei8.bas | 9 + tests/functional/arch/zx81sd/leu8.asm | 116 + tests/functional/arch/zx81sd/leu8.bas | 9 + tests/functional/arch/zx81sd/ltf.asm | 2631 +++++++ tests/functional/arch/zx81sd/ltf.bas | 8 + tests/functional/arch/zx81sd/ltf16.asm | 205 + tests/functional/arch/zx81sd/ltf16.bas | 8 + tests/functional/arch/zx81sd/lti16.asm | 159 + tests/functional/arch/zx81sd/lti16.bas | 9 + tests/functional/arch/zx81sd/lti32.asm | 218 + tests/functional/arch/zx81sd/lti32.bas | 9 + tests/functional/arch/zx81sd/lti8.asm | 127 + tests/functional/arch/zx81sd/lti8.bas | 9 + tests/functional/arch/zx81sd/ltu16.asm | 132 + tests/functional/arch/zx81sd/ltu16.bas | 9 + tests/functional/arch/zx81sd/ltu32.asm | 205 + tests/functional/arch/zx81sd/ltu32.bas | 9 + tests/functional/arch/zx81sd/ltu8.asm | 112 + tests/functional/arch/zx81sd/ltu8.bas | 9 + tests/functional/arch/zx81sd/math_acs.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_acs.bas | 2 + tests/functional/arch/zx81sd/math_asn.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_asn.bas | 2 + tests/functional/arch/zx81sd/math_atn.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_atn.bas | 2 + tests/functional/arch/zx81sd/math_cos.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_cos.bas | 2 + tests/functional/arch/zx81sd/math_exp.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_exp.bas | 2 + tests/functional/arch/zx81sd/math_ln.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_ln.bas | 2 + tests/functional/arch/zx81sd/math_sin.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_sin.bas | 2 + tests/functional/arch/zx81sd/math_sqr.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_sqr.bas | 2 + tests/functional/arch/zx81sd/math_tan.asm | 2372 ++++++ tests/functional/arch/zx81sd/math_tan.bas | 2 + tests/functional/arch/zx81sd/modf.asm | 2413 +++++++ tests/functional/arch/zx81sd/modf.bas | 3 + tests/functional/arch/zx81sd/modf16.asm | 81 + tests/functional/arch/zx81sd/modf16.bas | 3 + tests/functional/arch/zx81sd/modf16c.asm | 484 ++ tests/functional/arch/zx81sd/modf16c.bas | 9 + tests/functional/arch/zx81sd/modi32c.asm | 291 + tests/functional/arch/zx81sd/modi32c.bas | 9 + tests/functional/arch/zx81sd/modi8.asm | 168 + tests/functional/arch/zx81sd/modi8.bas | 13 + tests/functional/arch/zx81sd/modi8a.asm | 146 + tests/functional/arch/zx81sd/modi8a.bas | 4 + tests/functional/arch/zx81sd/modi8b.asm | 152 + tests/functional/arch/zx81sd/modi8b.bas | 4 + tests/functional/arch/zx81sd/modu32c.asm | 291 + tests/functional/arch/zx81sd/modu32c.bas | 9 + tests/functional/arch/zx81sd/modu8.asm | 168 + tests/functional/arch/zx81sd/modu8.bas | 13 + tests/functional/arch/zx81sd/modu8a.asm | 146 + tests/functional/arch/zx81sd/modu8a.bas | 4 + tests/functional/arch/zx81sd/modu8b.asm | 152 + tests/functional/arch/zx81sd/modu8b.bas | 4 + tests/functional/arch/zx81sd/mul16.asm | 115 + tests/functional/arch/zx81sd/mul16.bas | 10 + tests/functional/arch/zx81sd/mul16a.asm | 108 + tests/functional/arch/zx81sd/mul16a.bas | 4 + tests/functional/arch/zx81sd/mul16b.asm | 114 + tests/functional/arch/zx81sd/mul16b.bas | 4 + tests/functional/arch/zx81sd/mul16c.asm | 105 + tests/functional/arch/zx81sd/mul16c.bas | 3 + tests/functional/arch/zx81sd/mul32.asm | 115 + tests/functional/arch/zx81sd/mul32.bas | 10 + tests/functional/arch/zx81sd/mul32a.asm | 108 + tests/functional/arch/zx81sd/mul32a.bas | 4 + tests/functional/arch/zx81sd/mul32b.asm | 114 + tests/functional/arch/zx81sd/mul32b.bas | 4 + tests/functional/arch/zx81sd/mul32c.asm | 105 + tests/functional/arch/zx81sd/mul32c.bas | 3 + tests/functional/arch/zx81sd/mul8.asm | 151 + tests/functional/arch/zx81sd/mul8.bas | 14 + tests/functional/arch/zx81sd/mul8a.asm | 128 + tests/functional/arch/zx81sd/mul8a.bas | 4 + tests/functional/arch/zx81sd/mul8b.asm | 134 + tests/functional/arch/zx81sd/mul8b.bas | 4 + tests/functional/arch/zx81sd/mulf00.asm | 2421 +++++++ tests/functional/arch/zx81sd/mulf00.bas | 4 + tests/functional/arch/zx81sd/mulf01.asm | 2397 +++++++ tests/functional/arch/zx81sd/mulf01.bas | 4 + tests/functional/arch/zx81sd/mulf16.asm | 229 + tests/functional/arch/zx81sd/mulf16.bas | 10 + tests/functional/arch/zx81sd/mulf16a.asm | 213 + tests/functional/arch/zx81sd/mulf16a.bas | 4 + tests/functional/arch/zx81sd/muli8.asm | 125 + tests/functional/arch/zx81sd/muli8.bas | 3 + tests/functional/arch/zx81sd/mulu16.asm | 105 + tests/functional/arch/zx81sd/mulu16.bas | 3 + tests/functional/arch/zx81sd/mulu8.asm | 125 + tests/functional/arch/zx81sd/mulu8.bas | 3 + tests/functional/arch/zx81sd/nef.asm | 2553 +++++++ tests/functional/arch/zx81sd/nef.bas | 3 + tests/functional/arch/zx81sd/nef16.asm | 111 + tests/functional/arch/zx81sd/nef16.bas | 3 + tests/functional/arch/zx81sd/nei16.asm | 84 + tests/functional/arch/zx81sd/nei16.bas | 3 + tests/functional/arch/zx81sd/nei32.asm | 111 + tests/functional/arch/zx81sd/nei32.bas | 3 + tests/functional/arch/zx81sd/nei8.asm | 79 + tests/functional/arch/zx81sd/nei8.bas | 3 + tests/functional/arch/zx81sd/neu16.asm | 84 + tests/functional/arch/zx81sd/neu16.bas | 3 + tests/functional/arch/zx81sd/neu32.asm | 111 + tests/functional/arch/zx81sd/neu32.bas | 3 + tests/functional/arch/zx81sd/neu8.asm | 79 + tests/functional/arch/zx81sd/neu8.bas | 3 + .../arch/zx81sd/opt1_beep_const.asm | 233 + .../arch/zx81sd/opt1_beep_const.bas | 3 + .../functional/arch/zx81sd/opt1_beep_var.asm | 2769 +++++++ .../functional/arch/zx81sd/opt1_beep_var.bas | 4 + .../functional/arch/zx81sd/opt2_epilogue.asm | 72 + .../functional/arch/zx81sd/opt2_epilogue.bas | 2 + tests/functional/arch/zx81sd/opt4_mul8.asm | 175 + tests/functional/arch/zx81sd/opt4_mul8.bas | 10 + tests/functional/arch/zx81sd/print_at2.asm | 3310 +++++++++ tests/functional/arch/zx81sd/print_at2.bas | 2 + tests/functional/arch/zx81sd/print_f.asm | 3808 ++++++++++ tests/functional/arch/zx81sd/print_f.bas | 2 + tests/functional/arch/zx81sd/print_i16.asm | 3450 +++++++++ tests/functional/arch/zx81sd/print_i16.bas | 2 + tests/functional/arch/zx81sd/print_i32.asm | 3504 +++++++++ tests/functional/arch/zx81sd/print_i32.bas | 2 + tests/functional/arch/zx81sd/print_i8.asm | 3424 +++++++++ tests/functional/arch/zx81sd/print_i8.bas | 2 + tests/functional/arch/zx81sd/print_tab2.asm | 3305 +++++++++ tests/functional/arch/zx81sd/print_tab2.bas | 3 + tests/functional/arch/zx81sd/print_u16.asm | 3452 +++++++++ tests/functional/arch/zx81sd/print_u16.bas | 2 + tests/functional/arch/zx81sd/print_u32.asm | 3506 +++++++++ tests/functional/arch/zx81sd/print_u32.bas | 2 + tests/functional/arch/zx81sd/print_u8.asm | 3426 +++++++++ tests/functional/arch/zx81sd/print_u8.bas | 2 + tests/functional/arch/zx81sd/sgnf.asm | 121 + tests/functional/arch/zx81sd/sgnf.bas | 2 + tests/functional/arch/zx81sd/sgnf16.asm | 119 + tests/functional/arch/zx81sd/sgnf16.bas | 2 + tests/functional/arch/zx81sd/sgni16.asm | 116 + tests/functional/arch/zx81sd/sgni16.bas | 2 + tests/functional/arch/zx81sd/sgni32.asm | 119 + tests/functional/arch/zx81sd/sgni32.bas | 2 + tests/functional/arch/zx81sd/sgni8.asm | 89 + tests/functional/arch/zx81sd/sgni8.bas | 2 + tests/functional/arch/zx81sd/sgnu16.asm | 89 + tests/functional/arch/zx81sd/sgnu16.bas | 2 + tests/functional/arch/zx81sd/sgnu32.asm | 94 + tests/functional/arch/zx81sd/sgnu32.bas | 2 + tests/functional/arch/zx81sd/sgnu8.asm | 87 + tests/functional/arch/zx81sd/sgnu8.bas | 2 + tests/functional/arch/zx81sd/shli16.asm | 95 + tests/functional/arch/zx81sd/shli16.bas | 7 + tests/functional/arch/zx81sd/shli32.asm | 115 + tests/functional/arch/zx81sd/shli32.bas | 7 + tests/functional/arch/zx81sd/shli8.asm | 93 + tests/functional/arch/zx81sd/shli8.bas | 7 + tests/functional/arch/zx81sd/shlu16.asm | 95 + tests/functional/arch/zx81sd/shlu16.bas | 7 + tests/functional/arch/zx81sd/shlu32.asm | 115 + tests/functional/arch/zx81sd/shlu32.bas | 7 + tests/functional/arch/zx81sd/shlu8.asm | 93 + tests/functional/arch/zx81sd/shlu8.bas | 7 + tests/functional/arch/zx81sd/shri16.asm | 97 + tests/functional/arch/zx81sd/shri16.bas | 7 + tests/functional/arch/zx81sd/shri32.asm | 115 + tests/functional/arch/zx81sd/shri32.bas | 7 + tests/functional/arch/zx81sd/shri8.asm | 93 + tests/functional/arch/zx81sd/shri8.bas | 7 + tests/functional/arch/zx81sd/shru16.asm | 97 + tests/functional/arch/zx81sd/shru16.bas | 7 + tests/functional/arch/zx81sd/shru32.asm | 115 + tests/functional/arch/zx81sd/shru32.bas | 7 + tests/functional/arch/zx81sd/shru8.asm | 93 + tests/functional/arch/zx81sd/shru8.bas | 7 + .../functional/arch/zx81sd/stdlib_SP_Fill.bas | 1 + tests/functional/arch/zx81sd/stdlib_alloc.asm | 3035 ++++++++ tests/functional/arch/zx81sd/stdlib_alloc.bas | 1 + tests/functional/arch/zx81sd/stdlib_attr.asm | 2505 +++++++ tests/functional/arch/zx81sd/stdlib_attr.bas | 1 + tests/functional/arch/zx81sd/stdlib_basic.asm | 2415 +++++++ tests/functional/arch/zx81sd/stdlib_basic.bas | 1 + .../arch/zx81sd/stdlib_clearbox.asm | 2390 ++++++ .../arch/zx81sd/stdlib_clearbox.bas | 1 + .../functional/arch/zx81sd/stdlib_csrlin.asm | 2448 +++++++ .../functional/arch/zx81sd/stdlib_csrlin.bas | 1 + tests/functional/arch/zx81sd/stdlib_hex.asm | 3040 ++++++++ tests/functional/arch/zx81sd/stdlib_hex.bas | 1 + .../functional/arch/zx81sd/stdlib_memcopy.asm | 166 + .../functional/arch/zx81sd/stdlib_memcopy.bas | 1 + tests/functional/arch/zx81sd/stdlib_pos.asm | 2448 +++++++ tests/functional/arch/zx81sd/stdlib_pos.bas | 1 + .../arch/zx81sd/stdlib_putchars.asm | 2861 ++++++++ .../arch/zx81sd/stdlib_putchars.bas | 1 + .../arch/zx81sd/stdlib_scrbuffer.asm | 2349 ++++++ .../arch/zx81sd/stdlib_scrbuffer.bas | 1 + .../functional/arch/zx81sd/stdlib_screen.asm | 2859 ++++++++ .../functional/arch/zx81sd/stdlib_screen.bas | 1 + .../functional/arch/zx81sd/stdlib_scroll.asm | 3278 +++++++++ .../functional/arch/zx81sd/stdlib_scroll.bas | 1 + .../arch/zx81sd/stdlib_spectranet.asm | 6392 +++++++++++++++++ .../arch/zx81sd/stdlib_spectranet.bas | 1 + .../arch/zx81sd/stdlib_winscroll.asm | 2774 +++++++ .../arch/zx81sd/stdlib_winscroll.bas | 1 + tests/functional/arch/zx81sd/sub16.asm | 96 + tests/functional/arch/zx81sd/sub16.bas | 10 + tests/functional/arch/zx81sd/sub16a.asm | 82 + tests/functional/arch/zx81sd/sub16a.bas | 4 + tests/functional/arch/zx81sd/sub16b.asm | 90 + tests/functional/arch/zx81sd/sub16b.bas | 4 + tests/functional/arch/zx81sd/sub8.asm | 92 + tests/functional/arch/zx81sd/sub8.bas | 10 + tests/functional/arch/zx81sd/sub8a.asm | 80 + tests/functional/arch/zx81sd/sub8a.bas | 4 + tests/functional/arch/zx81sd/sub8b.asm | 86 + tests/functional/arch/zx81sd/sub8b.bas | 4 + tests/functional/arch/zx81sd/subf00.asm | 2422 +++++++ tests/functional/arch/zx81sd/subf00.bas | 4 + tests/functional/arch/zx81sd/subf01.asm | 2398 +++++++ tests/functional/arch/zx81sd/subf01.bas | 4 + tests/functional/arch/zx81sd/subf16c.asm | 157 + tests/functional/arch/zx81sd/subf16c.bas | 7 + tests/functional/arch/zx81sd/subi32c.asm | 166 + tests/functional/arch/zx81sd/subi32c.bas | 8 + tests/functional/arch/zx81sd/ubound0.asm | 96 + tests/functional/arch/zx81sd/ubound0.bas | 4 + tests/functional/arch/zx81sd/ubound12.asm | 3154 ++++++++ tests/functional/arch/zx81sd/ubound12.bas | 21 + 401 files changed, 197476 insertions(+) create mode 100644 tests/functional/arch/zx81sd/add16.asm create mode 100644 tests/functional/arch/zx81sd/add16.bas create mode 100644 tests/functional/arch/zx81sd/add16a.asm create mode 100644 tests/functional/arch/zx81sd/add16a.bas create mode 100644 tests/functional/arch/zx81sd/add16b.asm create mode 100644 tests/functional/arch/zx81sd/add16b.bas create mode 100644 tests/functional/arch/zx81sd/add32.asm create mode 100644 tests/functional/arch/zx81sd/add32.bas create mode 100644 tests/functional/arch/zx81sd/add32a.asm create mode 100644 tests/functional/arch/zx81sd/add32a.bas create mode 100644 tests/functional/arch/zx81sd/add32b.asm create mode 100644 tests/functional/arch/zx81sd/add32b.bas create mode 100644 tests/functional/arch/zx81sd/add8.asm create mode 100644 tests/functional/arch/zx81sd/add8.bas create mode 100644 tests/functional/arch/zx81sd/add8a.asm create mode 100644 tests/functional/arch/zx81sd/add8a.bas create mode 100644 tests/functional/arch/zx81sd/add8b.asm create mode 100644 tests/functional/arch/zx81sd/add8b.bas create mode 100644 tests/functional/arch/zx81sd/addf.asm create mode 100644 tests/functional/arch/zx81sd/addf.bas create mode 100644 tests/functional/arch/zx81sd/addf16.asm create mode 100644 tests/functional/arch/zx81sd/addf16.bas create mode 100644 tests/functional/arch/zx81sd/addf16a.asm create mode 100644 tests/functional/arch/zx81sd/addf16a.bas create mode 100644 tests/functional/arch/zx81sd/addf16b.asm create mode 100644 tests/functional/arch/zx81sd/addf16b.bas create mode 100644 tests/functional/arch/zx81sd/addstr.asm create mode 100644 tests/functional/arch/zx81sd/addstr.bas create mode 100644 tests/functional/arch/zx81sd/array00.asm create mode 100644 tests/functional/arch/zx81sd/array00.bas create mode 100644 tests/functional/arch/zx81sd/array01.asm create mode 100644 tests/functional/arch/zx81sd/array01.bas create mode 100644 tests/functional/arch/zx81sd/array02.asm create mode 100644 tests/functional/arch/zx81sd/array02.bas create mode 100644 tests/functional/arch/zx81sd/array03.asm create mode 100644 tests/functional/arch/zx81sd/array03.bas create mode 100644 tests/functional/arch/zx81sd/array04.asm create mode 100644 tests/functional/arch/zx81sd/array04.bas create mode 100644 tests/functional/arch/zx81sd/array05.asm create mode 100644 tests/functional/arch/zx81sd/array05.bas create mode 100644 tests/functional/arch/zx81sd/array06.asm create mode 100644 tests/functional/arch/zx81sd/array06.bas create mode 100644 tests/functional/arch/zx81sd/array07.asm create mode 100644 tests/functional/arch/zx81sd/array07.bas create mode 100644 tests/functional/arch/zx81sd/array08.asm create mode 100644 tests/functional/arch/zx81sd/array08.bas create mode 100644 tests/functional/arch/zx81sd/array09.asm create mode 100644 tests/functional/arch/zx81sd/array09.bas create mode 100644 tests/functional/arch/zx81sd/arraycopy0.asm create mode 100644 tests/functional/arch/zx81sd/arraycopy0.bas create mode 100644 tests/functional/arch/zx81sd/arraycopy1.asm create mode 100644 tests/functional/arch/zx81sd/arraycopy1.bas create mode 100644 tests/functional/arch/zx81sd/arraycopy2.asm create mode 100644 tests/functional/arch/zx81sd/arraycopy2.bas create mode 100644 tests/functional/arch/zx81sd/arraycopy3.asm create mode 100644 tests/functional/arch/zx81sd/arraycopy3.bas create mode 100644 tests/functional/arch/zx81sd/arraycopy4.asm create mode 100644 tests/functional/arch/zx81sd/arraycopy4.bas create mode 100644 tests/functional/arch/zx81sd/arraycopy5.asm create mode 100644 tests/functional/arch/zx81sd/arraycopy5.bas create mode 100644 tests/functional/arch/zx81sd/bound00.asm create mode 100644 tests/functional/arch/zx81sd/bound00.bas create mode 100644 tests/functional/arch/zx81sd/bound01.asm create mode 100644 tests/functional/arch/zx81sd/bound01.bas create mode 100644 tests/functional/arch/zx81sd/bound02.asm create mode 100644 tests/functional/arch/zx81sd/bound02.bas create mode 100644 tests/functional/arch/zx81sd/bound03.asm create mode 100644 tests/functional/arch/zx81sd/bound03.bas create mode 100644 tests/functional/arch/zx81sd/bound04.asm create mode 100644 tests/functional/arch/zx81sd/bound04.bas create mode 100644 tests/functional/arch/zx81sd/cast_const_to_int.asm create mode 100644 tests/functional/arch/zx81sd/cast_const_to_int.bas create mode 100644 tests/functional/arch/zx81sd/cast_f16_to_long.asm create mode 100644 tests/functional/arch/zx81sd/cast_f16_to_long.bas create mode 100644 tests/functional/arch/zx81sd/cast_f16_to_ulong.asm create mode 100644 tests/functional/arch/zx81sd/cast_f16_to_ulong.bas create mode 100644 tests/functional/arch/zx81sd/div32.asm create mode 100644 tests/functional/arch/zx81sd/div32.bas create mode 100644 tests/functional/arch/zx81sd/divf00.asm create mode 100644 tests/functional/arch/zx81sd/divf00.bas create mode 100644 tests/functional/arch/zx81sd/divf01.asm create mode 100644 tests/functional/arch/zx81sd/divf01.bas create mode 100644 tests/functional/arch/zx81sd/divf16.asm create mode 100644 tests/functional/arch/zx81sd/divf16.bas create mode 100644 tests/functional/arch/zx81sd/divf16a.asm create mode 100644 tests/functional/arch/zx81sd/divf16a.bas create mode 100644 tests/functional/arch/zx81sd/divf16b.asm create mode 100644 tests/functional/arch/zx81sd/divf16b.bas create mode 100644 tests/functional/arch/zx81sd/divf16c.asm create mode 100644 tests/functional/arch/zx81sd/divf16c.bas create mode 100644 tests/functional/arch/zx81sd/divi16a.asm create mode 100644 tests/functional/arch/zx81sd/divi16a.bas create mode 100644 tests/functional/arch/zx81sd/divi16b.asm create mode 100644 tests/functional/arch/zx81sd/divi16b.bas create mode 100644 tests/functional/arch/zx81sd/divi32c.asm create mode 100644 tests/functional/arch/zx81sd/divi32c.bas create mode 100644 tests/functional/arch/zx81sd/divi8.asm create mode 100644 tests/functional/arch/zx81sd/divi8.bas create mode 100644 tests/functional/arch/zx81sd/divi8a.asm create mode 100644 tests/functional/arch/zx81sd/divi8a.bas create mode 100644 tests/functional/arch/zx81sd/divi8b.asm create mode 100644 tests/functional/arch/zx81sd/divi8b.bas create mode 100644 tests/functional/arch/zx81sd/divu16.asm create mode 100644 tests/functional/arch/zx81sd/divu16.bas create mode 100644 tests/functional/arch/zx81sd/divu16a.asm create mode 100644 tests/functional/arch/zx81sd/divu16a.bas create mode 100644 tests/functional/arch/zx81sd/divu16b.asm create mode 100644 tests/functional/arch/zx81sd/divu16b.bas create mode 100644 tests/functional/arch/zx81sd/divu32c.asm create mode 100644 tests/functional/arch/zx81sd/divu32c.bas create mode 100644 tests/functional/arch/zx81sd/divu8.asm create mode 100644 tests/functional/arch/zx81sd/divu8.bas create mode 100644 tests/functional/arch/zx81sd/divu8a.asm create mode 100644 tests/functional/arch/zx81sd/divu8a.bas create mode 100644 tests/functional/arch/zx81sd/divu8b.asm create mode 100644 tests/functional/arch/zx81sd/divu8b.bas create mode 100644 tests/functional/arch/zx81sd/equ16.asm create mode 100644 tests/functional/arch/zx81sd/equ16.bas create mode 100644 tests/functional/arch/zx81sd/equ32.asm create mode 100644 tests/functional/arch/zx81sd/equ32.bas create mode 100644 tests/functional/arch/zx81sd/equ8.asm create mode 100644 tests/functional/arch/zx81sd/equ8.bas create mode 100644 tests/functional/arch/zx81sd/equf.asm create mode 100644 tests/functional/arch/zx81sd/equf.bas create mode 100644 tests/functional/arch/zx81sd/fact.asm create mode 100644 tests/functional/arch/zx81sd/fact.bas create mode 100644 tests/functional/arch/zx81sd/fp_pow.asm create mode 100644 tests/functional/arch/zx81sd/fp_pow.bas create mode 100644 tests/functional/arch/zx81sd/gef.asm create mode 100644 tests/functional/arch/zx81sd/gef.bas create mode 100644 tests/functional/arch/zx81sd/gef16.asm create mode 100644 tests/functional/arch/zx81sd/gef16.bas create mode 100644 tests/functional/arch/zx81sd/gei16.asm create mode 100644 tests/functional/arch/zx81sd/gei16.bas create mode 100644 tests/functional/arch/zx81sd/gei32.asm create mode 100644 tests/functional/arch/zx81sd/gei32.bas create mode 100644 tests/functional/arch/zx81sd/gei8.asm create mode 100644 tests/functional/arch/zx81sd/gei8.bas create mode 100644 tests/functional/arch/zx81sd/geu16.asm create mode 100644 tests/functional/arch/zx81sd/geu16.bas create mode 100644 tests/functional/arch/zx81sd/geu32.asm create mode 100644 tests/functional/arch/zx81sd/geu32.bas create mode 100644 tests/functional/arch/zx81sd/geu8.asm create mode 100644 tests/functional/arch/zx81sd/geu8.bas create mode 100644 tests/functional/arch/zx81sd/gtf.asm create mode 100644 tests/functional/arch/zx81sd/gtf.bas create mode 100644 tests/functional/arch/zx81sd/gtf16.asm create mode 100644 tests/functional/arch/zx81sd/gtf16.bas create mode 100644 tests/functional/arch/zx81sd/gti16.asm create mode 100644 tests/functional/arch/zx81sd/gti16.bas create mode 100644 tests/functional/arch/zx81sd/gti32.asm create mode 100644 tests/functional/arch/zx81sd/gti32.bas create mode 100644 tests/functional/arch/zx81sd/gti8.asm create mode 100644 tests/functional/arch/zx81sd/gti8.bas create mode 100644 tests/functional/arch/zx81sd/gtu16.asm create mode 100644 tests/functional/arch/zx81sd/gtu16.bas create mode 100644 tests/functional/arch/zx81sd/gtu32.asm create mode 100644 tests/functional/arch/zx81sd/gtu32.bas create mode 100644 tests/functional/arch/zx81sd/gtu8.asm create mode 100644 tests/functional/arch/zx81sd/gtu8.bas create mode 100644 tests/functional/arch/zx81sd/inkey.asm create mode 100644 tests/functional/arch/zx81sd/inkey.bas create mode 100644 tests/functional/arch/zx81sd/lbound12.asm create mode 100644 tests/functional/arch/zx81sd/lbound12.bas create mode 100644 tests/functional/arch/zx81sd/lef.asm create mode 100644 tests/functional/arch/zx81sd/lef.bas create mode 100644 tests/functional/arch/zx81sd/lei16.asm create mode 100644 tests/functional/arch/zx81sd/lei16.bas create mode 100644 tests/functional/arch/zx81sd/lei32.asm create mode 100644 tests/functional/arch/zx81sd/lei32.bas create mode 100644 tests/functional/arch/zx81sd/lei8.asm create mode 100644 tests/functional/arch/zx81sd/lei8.bas create mode 100644 tests/functional/arch/zx81sd/leu8.asm create mode 100644 tests/functional/arch/zx81sd/leu8.bas create mode 100644 tests/functional/arch/zx81sd/ltf.asm create mode 100644 tests/functional/arch/zx81sd/ltf.bas create mode 100644 tests/functional/arch/zx81sd/ltf16.asm create mode 100644 tests/functional/arch/zx81sd/ltf16.bas create mode 100644 tests/functional/arch/zx81sd/lti16.asm create mode 100644 tests/functional/arch/zx81sd/lti16.bas create mode 100644 tests/functional/arch/zx81sd/lti32.asm create mode 100644 tests/functional/arch/zx81sd/lti32.bas create mode 100644 tests/functional/arch/zx81sd/lti8.asm create mode 100644 tests/functional/arch/zx81sd/lti8.bas create mode 100644 tests/functional/arch/zx81sd/ltu16.asm create mode 100644 tests/functional/arch/zx81sd/ltu16.bas create mode 100644 tests/functional/arch/zx81sd/ltu32.asm create mode 100644 tests/functional/arch/zx81sd/ltu32.bas create mode 100644 tests/functional/arch/zx81sd/ltu8.asm create mode 100644 tests/functional/arch/zx81sd/ltu8.bas create mode 100644 tests/functional/arch/zx81sd/math_acs.asm create mode 100644 tests/functional/arch/zx81sd/math_acs.bas create mode 100644 tests/functional/arch/zx81sd/math_asn.asm create mode 100644 tests/functional/arch/zx81sd/math_asn.bas create mode 100644 tests/functional/arch/zx81sd/math_atn.asm create mode 100644 tests/functional/arch/zx81sd/math_atn.bas create mode 100644 tests/functional/arch/zx81sd/math_cos.asm create mode 100644 tests/functional/arch/zx81sd/math_cos.bas create mode 100644 tests/functional/arch/zx81sd/math_exp.asm create mode 100644 tests/functional/arch/zx81sd/math_exp.bas create mode 100644 tests/functional/arch/zx81sd/math_ln.asm create mode 100644 tests/functional/arch/zx81sd/math_ln.bas create mode 100644 tests/functional/arch/zx81sd/math_sin.asm create mode 100644 tests/functional/arch/zx81sd/math_sin.bas create mode 100644 tests/functional/arch/zx81sd/math_sqr.asm create mode 100644 tests/functional/arch/zx81sd/math_sqr.bas create mode 100644 tests/functional/arch/zx81sd/math_tan.asm create mode 100644 tests/functional/arch/zx81sd/math_tan.bas create mode 100644 tests/functional/arch/zx81sd/modf.asm create mode 100644 tests/functional/arch/zx81sd/modf.bas create mode 100644 tests/functional/arch/zx81sd/modf16.asm create mode 100644 tests/functional/arch/zx81sd/modf16.bas create mode 100644 tests/functional/arch/zx81sd/modf16c.asm create mode 100644 tests/functional/arch/zx81sd/modf16c.bas create mode 100644 tests/functional/arch/zx81sd/modi32c.asm create mode 100644 tests/functional/arch/zx81sd/modi32c.bas create mode 100644 tests/functional/arch/zx81sd/modi8.asm create mode 100644 tests/functional/arch/zx81sd/modi8.bas create mode 100644 tests/functional/arch/zx81sd/modi8a.asm create mode 100644 tests/functional/arch/zx81sd/modi8a.bas create mode 100644 tests/functional/arch/zx81sd/modi8b.asm create mode 100644 tests/functional/arch/zx81sd/modi8b.bas create mode 100644 tests/functional/arch/zx81sd/modu32c.asm create mode 100644 tests/functional/arch/zx81sd/modu32c.bas create mode 100644 tests/functional/arch/zx81sd/modu8.asm create mode 100644 tests/functional/arch/zx81sd/modu8.bas create mode 100644 tests/functional/arch/zx81sd/modu8a.asm create mode 100644 tests/functional/arch/zx81sd/modu8a.bas create mode 100644 tests/functional/arch/zx81sd/modu8b.asm create mode 100644 tests/functional/arch/zx81sd/modu8b.bas create mode 100644 tests/functional/arch/zx81sd/mul16.asm create mode 100644 tests/functional/arch/zx81sd/mul16.bas create mode 100644 tests/functional/arch/zx81sd/mul16a.asm create mode 100644 tests/functional/arch/zx81sd/mul16a.bas create mode 100644 tests/functional/arch/zx81sd/mul16b.asm create mode 100644 tests/functional/arch/zx81sd/mul16b.bas create mode 100644 tests/functional/arch/zx81sd/mul16c.asm create mode 100644 tests/functional/arch/zx81sd/mul16c.bas create mode 100644 tests/functional/arch/zx81sd/mul32.asm create mode 100644 tests/functional/arch/zx81sd/mul32.bas create mode 100644 tests/functional/arch/zx81sd/mul32a.asm create mode 100644 tests/functional/arch/zx81sd/mul32a.bas create mode 100644 tests/functional/arch/zx81sd/mul32b.asm create mode 100644 tests/functional/arch/zx81sd/mul32b.bas create mode 100644 tests/functional/arch/zx81sd/mul32c.asm create mode 100644 tests/functional/arch/zx81sd/mul32c.bas create mode 100644 tests/functional/arch/zx81sd/mul8.asm create mode 100644 tests/functional/arch/zx81sd/mul8.bas create mode 100644 tests/functional/arch/zx81sd/mul8a.asm create mode 100644 tests/functional/arch/zx81sd/mul8a.bas create mode 100644 tests/functional/arch/zx81sd/mul8b.asm create mode 100644 tests/functional/arch/zx81sd/mul8b.bas create mode 100644 tests/functional/arch/zx81sd/mulf00.asm create mode 100644 tests/functional/arch/zx81sd/mulf00.bas create mode 100644 tests/functional/arch/zx81sd/mulf01.asm create mode 100644 tests/functional/arch/zx81sd/mulf01.bas create mode 100644 tests/functional/arch/zx81sd/mulf16.asm create mode 100644 tests/functional/arch/zx81sd/mulf16.bas create mode 100644 tests/functional/arch/zx81sd/mulf16a.asm create mode 100644 tests/functional/arch/zx81sd/mulf16a.bas create mode 100644 tests/functional/arch/zx81sd/muli8.asm create mode 100644 tests/functional/arch/zx81sd/muli8.bas create mode 100644 tests/functional/arch/zx81sd/mulu16.asm create mode 100644 tests/functional/arch/zx81sd/mulu16.bas create mode 100644 tests/functional/arch/zx81sd/mulu8.asm create mode 100644 tests/functional/arch/zx81sd/mulu8.bas create mode 100644 tests/functional/arch/zx81sd/nef.asm create mode 100644 tests/functional/arch/zx81sd/nef.bas create mode 100644 tests/functional/arch/zx81sd/nef16.asm create mode 100644 tests/functional/arch/zx81sd/nef16.bas create mode 100644 tests/functional/arch/zx81sd/nei16.asm create mode 100644 tests/functional/arch/zx81sd/nei16.bas create mode 100644 tests/functional/arch/zx81sd/nei32.asm create mode 100644 tests/functional/arch/zx81sd/nei32.bas create mode 100644 tests/functional/arch/zx81sd/nei8.asm create mode 100644 tests/functional/arch/zx81sd/nei8.bas create mode 100644 tests/functional/arch/zx81sd/neu16.asm create mode 100644 tests/functional/arch/zx81sd/neu16.bas create mode 100644 tests/functional/arch/zx81sd/neu32.asm create mode 100644 tests/functional/arch/zx81sd/neu32.bas create mode 100644 tests/functional/arch/zx81sd/neu8.asm create mode 100644 tests/functional/arch/zx81sd/neu8.bas create mode 100644 tests/functional/arch/zx81sd/opt1_beep_const.asm create mode 100644 tests/functional/arch/zx81sd/opt1_beep_const.bas create mode 100644 tests/functional/arch/zx81sd/opt1_beep_var.asm create mode 100644 tests/functional/arch/zx81sd/opt1_beep_var.bas create mode 100644 tests/functional/arch/zx81sd/opt2_epilogue.asm create mode 100644 tests/functional/arch/zx81sd/opt2_epilogue.bas create mode 100644 tests/functional/arch/zx81sd/opt4_mul8.asm create mode 100644 tests/functional/arch/zx81sd/opt4_mul8.bas create mode 100644 tests/functional/arch/zx81sd/print_at2.asm create mode 100644 tests/functional/arch/zx81sd/print_at2.bas create mode 100644 tests/functional/arch/zx81sd/print_f.asm create mode 100644 tests/functional/arch/zx81sd/print_f.bas create mode 100644 tests/functional/arch/zx81sd/print_i16.asm create mode 100644 tests/functional/arch/zx81sd/print_i16.bas create mode 100644 tests/functional/arch/zx81sd/print_i32.asm create mode 100644 tests/functional/arch/zx81sd/print_i32.bas create mode 100644 tests/functional/arch/zx81sd/print_i8.asm create mode 100644 tests/functional/arch/zx81sd/print_i8.bas create mode 100644 tests/functional/arch/zx81sd/print_tab2.asm create mode 100644 tests/functional/arch/zx81sd/print_tab2.bas create mode 100644 tests/functional/arch/zx81sd/print_u16.asm create mode 100644 tests/functional/arch/zx81sd/print_u16.bas create mode 100644 tests/functional/arch/zx81sd/print_u32.asm create mode 100644 tests/functional/arch/zx81sd/print_u32.bas create mode 100644 tests/functional/arch/zx81sd/print_u8.asm create mode 100644 tests/functional/arch/zx81sd/print_u8.bas create mode 100644 tests/functional/arch/zx81sd/sgnf.asm create mode 100644 tests/functional/arch/zx81sd/sgnf.bas create mode 100644 tests/functional/arch/zx81sd/sgnf16.asm create mode 100644 tests/functional/arch/zx81sd/sgnf16.bas create mode 100644 tests/functional/arch/zx81sd/sgni16.asm create mode 100644 tests/functional/arch/zx81sd/sgni16.bas create mode 100644 tests/functional/arch/zx81sd/sgni32.asm create mode 100644 tests/functional/arch/zx81sd/sgni32.bas create mode 100644 tests/functional/arch/zx81sd/sgni8.asm create mode 100644 tests/functional/arch/zx81sd/sgni8.bas create mode 100644 tests/functional/arch/zx81sd/sgnu16.asm create mode 100644 tests/functional/arch/zx81sd/sgnu16.bas create mode 100644 tests/functional/arch/zx81sd/sgnu32.asm create mode 100644 tests/functional/arch/zx81sd/sgnu32.bas create mode 100644 tests/functional/arch/zx81sd/sgnu8.asm create mode 100644 tests/functional/arch/zx81sd/sgnu8.bas create mode 100644 tests/functional/arch/zx81sd/shli16.asm create mode 100644 tests/functional/arch/zx81sd/shli16.bas create mode 100644 tests/functional/arch/zx81sd/shli32.asm create mode 100644 tests/functional/arch/zx81sd/shli32.bas create mode 100644 tests/functional/arch/zx81sd/shli8.asm create mode 100644 tests/functional/arch/zx81sd/shli8.bas create mode 100644 tests/functional/arch/zx81sd/shlu16.asm create mode 100644 tests/functional/arch/zx81sd/shlu16.bas create mode 100644 tests/functional/arch/zx81sd/shlu32.asm create mode 100644 tests/functional/arch/zx81sd/shlu32.bas create mode 100644 tests/functional/arch/zx81sd/shlu8.asm create mode 100644 tests/functional/arch/zx81sd/shlu8.bas create mode 100644 tests/functional/arch/zx81sd/shri16.asm create mode 100644 tests/functional/arch/zx81sd/shri16.bas create mode 100644 tests/functional/arch/zx81sd/shri32.asm create mode 100644 tests/functional/arch/zx81sd/shri32.bas create mode 100644 tests/functional/arch/zx81sd/shri8.asm create mode 100644 tests/functional/arch/zx81sd/shri8.bas create mode 100644 tests/functional/arch/zx81sd/shru16.asm create mode 100644 tests/functional/arch/zx81sd/shru16.bas create mode 100644 tests/functional/arch/zx81sd/shru32.asm create mode 100644 tests/functional/arch/zx81sd/shru32.bas create mode 100644 tests/functional/arch/zx81sd/shru8.asm create mode 100644 tests/functional/arch/zx81sd/shru8.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_SP_Fill.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_alloc.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_alloc.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_attr.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_attr.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_basic.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_basic.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_clearbox.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_clearbox.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_csrlin.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_csrlin.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_hex.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_hex.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_memcopy.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_memcopy.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_pos.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_pos.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_putchars.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_putchars.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_scrbuffer.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_scrbuffer.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_screen.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_screen.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_scroll.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_scroll.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_spectranet.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_spectranet.bas create mode 100644 tests/functional/arch/zx81sd/stdlib_winscroll.asm create mode 100644 tests/functional/arch/zx81sd/stdlib_winscroll.bas create mode 100644 tests/functional/arch/zx81sd/sub16.asm create mode 100644 tests/functional/arch/zx81sd/sub16.bas create mode 100644 tests/functional/arch/zx81sd/sub16a.asm create mode 100644 tests/functional/arch/zx81sd/sub16a.bas create mode 100644 tests/functional/arch/zx81sd/sub16b.asm create mode 100644 tests/functional/arch/zx81sd/sub16b.bas create mode 100644 tests/functional/arch/zx81sd/sub8.asm create mode 100644 tests/functional/arch/zx81sd/sub8.bas create mode 100644 tests/functional/arch/zx81sd/sub8a.asm create mode 100644 tests/functional/arch/zx81sd/sub8a.bas create mode 100644 tests/functional/arch/zx81sd/sub8b.asm create mode 100644 tests/functional/arch/zx81sd/sub8b.bas create mode 100644 tests/functional/arch/zx81sd/subf00.asm create mode 100644 tests/functional/arch/zx81sd/subf00.bas create mode 100644 tests/functional/arch/zx81sd/subf01.asm create mode 100644 tests/functional/arch/zx81sd/subf01.bas create mode 100644 tests/functional/arch/zx81sd/subf16c.asm create mode 100644 tests/functional/arch/zx81sd/subf16c.bas create mode 100644 tests/functional/arch/zx81sd/subi32c.asm create mode 100644 tests/functional/arch/zx81sd/subi32c.bas create mode 100644 tests/functional/arch/zx81sd/ubound0.asm create mode 100644 tests/functional/arch/zx81sd/ubound0.bas create mode 100644 tests/functional/arch/zx81sd/ubound12.asm create mode 100644 tests/functional/arch/zx81sd/ubound12.bas diff --git a/tests/functional/arch/zx81sd/add16.asm b/tests/functional/arch/zx81sd/add16.asm new file mode 100644 index 000000000..0b33bc994 --- /dev/null +++ b/tests/functional/arch/zx81sd/add16.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add16.bas b/tests/functional/arch/zx81sd/add16.bas new file mode 100644 index 000000000..f2273e9f2 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/add16a.asm b/tests/functional/arch/zx81sd/add16a.asm new file mode 100644 index 000000000..62c1ad89f --- /dev/null +++ b/tests/functional/arch/zx81sd/add16a.asm @@ -0,0 +1,81 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add16a.bas b/tests/functional/arch/zx81sd/add16a.bas new file mode 100644 index 000000000..37b6bf698 --- /dev/null +++ b/tests/functional/arch/zx81sd/add16a.bas @@ -0,0 +1,4 @@ + +REM another ADD16 tests +DIM a As Integer +a = a + a + a diff --git a/tests/functional/arch/zx81sd/add16b.asm b/tests/functional/arch/zx81sd/add16b.asm new file mode 100644 index 000000000..a163b284c --- /dev/null +++ b/tests/functional/arch/zx81sd/add16b.asm @@ -0,0 +1,88 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add16b.bas b/tests/functional/arch/zx81sd/add16b.bas new file mode 100644 index 000000000..30b684b21 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/add32.asm b/tests/functional/arch/zx81sd/add32.asm new file mode 100644 index 000000000..8d006cb0f --- /dev/null +++ b/tests/functional/arch/zx81sd/add32.asm @@ -0,0 +1,114 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add32.bas b/tests/functional/arch/zx81sd/add32.bas new file mode 100644 index 000000000..db03bf26d --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/add32a.asm b/tests/functional/arch/zx81sd/add32a.asm new file mode 100644 index 000000000..03cbba712 --- /dev/null +++ b/tests/functional/arch/zx81sd/add32a.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add32a.bas b/tests/functional/arch/zx81sd/add32a.bas new file mode 100644 index 000000000..94452fb0c --- /dev/null +++ b/tests/functional/arch/zx81sd/add32a.bas @@ -0,0 +1,4 @@ + +REM another ADD32 test +DIM a As ULong +a = a + a + a diff --git a/tests/functional/arch/zx81sd/add32b.asm b/tests/functional/arch/zx81sd/add32b.asm new file mode 100644 index 000000000..fbd20eae0 --- /dev/null +++ b/tests/functional/arch/zx81sd/add32b.asm @@ -0,0 +1,105 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add32b.bas b/tests/functional/arch/zx81sd/add32b.bas new file mode 100644 index 000000000..32ed2f068 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/add8.asm b/tests/functional/arch/zx81sd/add8.asm new file mode 100644 index 000000000..284ff6030 --- /dev/null +++ b/tests/functional/arch/zx81sd/add8.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add8.bas b/tests/functional/arch/zx81sd/add8.bas new file mode 100644 index 000000000..66403c245 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/add8a.asm b/tests/functional/arch/zx81sd/add8a.asm new file mode 100644 index 000000000..888d96e4d --- /dev/null +++ b/tests/functional/arch/zx81sd/add8a.asm @@ -0,0 +1,81 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add8a.bas b/tests/functional/arch/zx81sd/add8a.bas new file mode 100644 index 000000000..98d639524 --- /dev/null +++ b/tests/functional/arch/zx81sd/add8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a + a + a diff --git a/tests/functional/arch/zx81sd/add8b.asm b/tests/functional/arch/zx81sd/add8b.asm new file mode 100644 index 000000000..67c16ce29 --- /dev/null +++ b/tests/functional/arch/zx81sd/add8b.asm @@ -0,0 +1,87 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/add8b.bas b/tests/functional/arch/zx81sd/add8b.bas new file mode 100644 index 000000000..fef66607a --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/addf.asm b/tests/functional/arch/zx81sd/addf.asm new file mode 100644 index 000000000..51b9e3be6 --- /dev/null +++ b/tests/functional/arch/zx81sd/addf.asm @@ -0,0 +1,2428 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#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. + ; 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 34 "arch/zx81sd/addf.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 35 "arch/zx81sd/addf.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 36 "arch/zx81sd/addf.bas" + END diff --git a/tests/functional/arch/zx81sd/addf.bas b/tests/functional/arch/zx81sd/addf.bas new file mode 100644 index 000000000..782365537 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/addf16.asm b/tests/functional/arch/zx81sd/addf16.asm new file mode 100644 index 000000000..79b4cd361 --- /dev/null +++ b/tests/functional/arch/zx81sd/addf16.asm @@ -0,0 +1,114 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/addf16.bas b/tests/functional/arch/zx81sd/addf16.bas new file mode 100644 index 000000000..8e068aab3 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/addf16a.asm b/tests/functional/arch/zx81sd/addf16a.asm new file mode 100644 index 000000000..03cbba712 --- /dev/null +++ b/tests/functional/arch/zx81sd/addf16a.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/addf16a.bas b/tests/functional/arch/zx81sd/addf16a.bas new file mode 100644 index 000000000..a97333ffe --- /dev/null +++ b/tests/functional/arch/zx81sd/addf16a.bas @@ -0,0 +1,4 @@ + +REM another ADDF16 tests +DIM a As Fixed +a = a + a + a diff --git a/tests/functional/arch/zx81sd/addf16b.asm b/tests/functional/arch/zx81sd/addf16b.asm new file mode 100644 index 000000000..b7e7e65a1 --- /dev/null +++ b/tests/functional/arch/zx81sd/addf16b.asm @@ -0,0 +1,105 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/addf16b.bas b/tests/functional/arch/zx81sd/addf16b.bas new file mode 100644 index 000000000..762129a31 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/addstr.asm b/tests/functional/arch/zx81sd/addstr.asm new file mode 100644 index 000000000..3a222d206 --- /dev/null +++ b/tests/functional/arch/zx81sd/addstr.asm @@ -0,0 +1,2941 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 + halt +.LABEL.__LABEL0: + DEFW 0000h +.LABEL.__LABEL1: + DEFW 0001h + DEFB 31h + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/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 44 "arch/zx81sd/addstr.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcat.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/strcat.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 45 "arch/zx81sd/addstr.bas" + END diff --git a/tests/functional/arch/zx81sd/addstr.bas b/tests/functional/arch/zx81sd/addstr.bas new file mode 100644 index 000000000..84ad57241 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array00.asm b/tests/functional/arch/zx81sd/array00.asm new file mode 100644 index 000000000..c32743a65 --- /dev/null +++ b/tests/functional/arch/zx81sd/array00.asm @@ -0,0 +1,113 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/array00.bas b/tests/functional/arch/zx81sd/array00.bas new file mode 100644 index 000000000..db2a4f65b --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array01.asm b/tests/functional/arch/zx81sd/array01.asm new file mode 100644 index 000000000..caae37819 --- /dev/null +++ b/tests/functional/arch/zx81sd/array01.asm @@ -0,0 +1,95 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/array01.bas b/tests/functional/arch/zx81sd/array01.bas new file mode 100644 index 000000000..9e77593ba --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array02.asm b/tests/functional/arch/zx81sd/array02.asm new file mode 100644 index 000000000..b6111f014 --- /dev/null +++ b/tests/functional/arch/zx81sd/array02.asm @@ -0,0 +1,97 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/array02.bas b/tests/functional/arch/zx81sd/array02.bas new file mode 100644 index 000000000..a86a99342 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array03.asm b/tests/functional/arch/zx81sd/array03.asm new file mode 100644 index 000000000..47a285a2f --- /dev/null +++ b/tests/functional/arch/zx81sd/array03.asm @@ -0,0 +1,2524 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ... + ; +; zx81sd override: the only change from zx48k's version is + ; LBOUND_PTR's storage address. The original uses the Spectrum ROM's + ; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- + ; on zx81sd that address falls inside the program's own compiled code + ; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting + ; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) + ; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + 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 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 27 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 28 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 32 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT + 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 74 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 124 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 154 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 164 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ENDP + pop namespace +#line 17 "arch/zx81sd/array03.bas" + END diff --git a/tests/functional/arch/zx81sd/array03.bas b/tests/functional/arch/zx81sd/array03.bas new file mode 100644 index 000000000..10fdfe0a4 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array04.asm b/tests/functional/arch/zx81sd/array04.asm new file mode 100644 index 000000000..72ba0e294 --- /dev/null +++ b/tests/functional/arch/zx81sd/array04.asm @@ -0,0 +1,106 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/array04.bas b/tests/functional/arch/zx81sd/array04.bas new file mode 100644 index 000000000..571f1b25c --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array05.asm b/tests/functional/arch/zx81sd/array05.asm new file mode 100644 index 000000000..96550beef --- /dev/null +++ b/tests/functional/arch/zx81sd/array05.asm @@ -0,0 +1,108 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/array05.bas b/tests/functional/arch/zx81sd/array05.bas new file mode 100644 index 000000000..2b9d662e4 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array06.asm b/tests/functional/arch/zx81sd/array06.asm new file mode 100644 index 000000000..8809d2477 --- /dev/null +++ b/tests/functional/arch/zx81sd/array06.asm @@ -0,0 +1,2535 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ... + ; +; zx81sd override: the only change from zx48k's version is + ; LBOUND_PTR's storage address. The original uses the Spectrum ROM's + ; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- + ; on zx81sd that address falls inside the program's own compiled code + ; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting + ; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) + ; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + 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 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 27 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 28 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 32 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT + 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 74 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 124 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 154 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 164 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ENDP + pop namespace +#line 17 "arch/zx81sd/array06.bas" + END diff --git a/tests/functional/arch/zx81sd/array06.bas b/tests/functional/arch/zx81sd/array06.bas new file mode 100644 index 000000000..61bebc351 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array07.asm b/tests/functional/arch/zx81sd/array07.asm new file mode 100644 index 000000000..5129f948b --- /dev/null +++ b/tests/functional/arch/zx81sd/array07.asm @@ -0,0 +1,3234 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 + halt +_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/zx81sd/runtime/array/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 + ... + ; +; zx81sd override: the only change from zx48k's version is + ; LBOUND_PTR's storage address. The original uses the Spectrum ROM's + ; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- + ; on zx81sd that address falls inside the program's own compiled code + ; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting + ; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) + ; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + 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 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 27 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 28 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 32 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT + 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 74 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 124 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 154 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 164 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ENDP + pop namespace +#line 37 "arch/zx81sd/array07.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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 38 "arch/zx81sd/array07.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/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/zx48k/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 39 "arch/zx81sd/array07.bas" + END diff --git a/tests/functional/arch/zx81sd/array07.bas b/tests/functional/arch/zx81sd/array07.bas new file mode 100644 index 000000000..fe859566f --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array08.asm b/tests/functional/arch/zx81sd/array08.asm new file mode 100644 index 000000000..c94432ec5 --- /dev/null +++ b/tests/functional/arch/zx81sd/array08.asm @@ -0,0 +1,3214 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ... + ; +; zx81sd override: the only change from zx48k's version is + ; LBOUND_PTR's storage address. The original uses the Spectrum ROM's + ; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- + ; on zx81sd that address falls inside the program's own compiled code + ; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting + ; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) + ; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + 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 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 27 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 28 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 32 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT + 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 74 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 124 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 154 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 164 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ENDP + pop namespace +#line 15 "arch/zx81sd/array08.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/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/zx48k/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 16 "arch/zx81sd/array08.bas" + END diff --git a/tests/functional/arch/zx81sd/array08.bas b/tests/functional/arch/zx81sd/array08.bas new file mode 100644 index 000000000..758ec007a --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/array09.asm b/tests/functional/arch/zx81sd/array09.asm new file mode 100644 index 000000000..e3b4edffc --- /dev/null +++ b/tests/functional/arch/zx81sd/array09.asm @@ -0,0 +1,3214 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ... + ; +; zx81sd override: the only change from zx48k's version is + ; LBOUND_PTR's storage address. The original uses the Spectrum ROM's + ; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- + ; on zx81sd that address falls inside the program's own compiled code + ; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting + ; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) + ; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + 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 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 27 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 28 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 32 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT + 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 74 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 124 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 154 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 164 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ENDP + pop namespace +#line 15 "arch/zx81sd/array09.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/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/zx48k/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 16 "arch/zx81sd/array09.bas" + END diff --git a/tests/functional/arch/zx81sd/array09.bas b/tests/functional/arch/zx81sd/array09.bas new file mode 100644 index 000000000..0d189285f --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/arraycopy0.asm b/tests/functional/arch/zx81sd/arraycopy0.asm new file mode 100644 index 000000000..9e794be56 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy0.asm @@ -0,0 +1,118 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_grid: + DEFW .LABEL.__LABEL0 +_grid.__DATA__.__PTR__: + DEFW _grid.__DATA__ + DEFW 0 + DEFW 0 +_grid.__DATA__: + DEFB 00h + DEFB 01h + DEFB 02h + DEFB 03h + DEFB 04h +.LABEL.__LABEL0: + DEFW 0000h + DEFB 01h +_gridcopy: + DEFW .LABEL.__LABEL1 +_gridcopy.__DATA__.__PTR__: + DEFW _gridcopy.__DATA__ + DEFW 0 + DEFW 0 +_gridcopy.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL1: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 5 + ld b, h + ld c, l + ld hl, _grid.__DATA__ + ld de, _gridcopy.__DATA__ + ldir + ld a, (_grid.__DATA__ + 0) + ld (_a), a + ld hl, 5 + ld b, h + ld c, l + ld hl, _grid.__DATA__ + ld de, _gridcopy.__DATA__ + ldir + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/arraycopy0.bas b/tests/functional/arch/zx81sd/arraycopy0.bas new file mode 100644 index 000000000..6e6e7a937 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy0.bas @@ -0,0 +1,8 @@ +DIM grid (4) as uByte => {0,1,2,3,4} +DIM gridcopy(4) as uByte + +gridcopy=grid + +a = grid(0) + +gridcopy=grid diff --git a/tests/functional/arch/zx81sd/arraycopy1.asm b/tests/functional/arch/zx81sd/arraycopy1.asm new file mode 100644 index 000000000..1aac46093 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy1.asm @@ -0,0 +1,2918 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +_grid: + DEFW .LABEL.__LABEL1 +_grid.__DATA__.__PTR__: + DEFW _grid.__DATA__ + DEFW 0 + DEFW 0 +_grid.__DATA__: + DEFB 00h + DEFB 01h + DEFB 02h + DEFB 03h + DEFB 04h +.LABEL.__LABEL1: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_Test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + ld hl, -6 + ld de, .LABEL.__LABEL0 + ld bc, 5 + call .core.__ALLOC_LOCAL_ARRAY + ld l, (ix-4) + ld h, (ix-3) + push hl + ld hl, 5 + ld b, h + ld c, l + ld hl, _grid.__DATA__ + pop de + ldir +_Test__leave: + ex af, af' + exx + ld l, (ix-4) + ld h, (ix-3) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 41 "arch/zx81sd/arraycopy1.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 42 "arch/zx81sd/arraycopy1.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h + END diff --git a/tests/functional/arch/zx81sd/arraycopy1.bas b/tests/functional/arch/zx81sd/arraycopy1.bas new file mode 100644 index 000000000..91b4db3a3 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy1.bas @@ -0,0 +1,6 @@ +DIM grid (4) as uByte => {0,1,2,3,4} + +SUB Test + DIM gridcopy(4) as uByte + gridcopy=grid +END SUB diff --git a/tests/functional/arch/zx81sd/arraycopy2.asm b/tests/functional/arch/zx81sd/arraycopy2.asm new file mode 100644 index 000000000..24b694d24 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy2.asm @@ -0,0 +1,2926 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +_gridcopy: + DEFW .LABEL.__LABEL2 +_gridcopy.__DATA__.__PTR__: + DEFW _gridcopy.__DATA__ + DEFW 0 + DEFW 0 +_gridcopy.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL2: + DEFW 0000h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_Test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + ld hl, .LABEL.__LABEL1 + push hl + ld hl, -6 + ld de, .LABEL.__LABEL0 + ld bc, 5 + call .core.__ALLOC_INITIALIZED_LOCAL_ARRAY + ld l, (ix-4) + ld h, (ix-3) + push hl + ld hl, 5 + ld b, h + ld c, l + pop hl + ld de, _gridcopy.__DATA__ + ldir +_Test__leave: + ex af, af' + exx + ld l, (ix-4) + ld h, (ix-3) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 43 "arch/zx81sd/arraycopy2.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 44 "arch/zx81sd/arraycopy2.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h +.LABEL.__LABEL1: + DEFB 00h + DEFB 01h + DEFB 02h + DEFB 03h + DEFB 04h + END diff --git a/tests/functional/arch/zx81sd/arraycopy2.bas b/tests/functional/arch/zx81sd/arraycopy2.bas new file mode 100644 index 000000000..6cb55f2c1 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy2.bas @@ -0,0 +1,5 @@ +DIM gridcopy(4) as uByte +SUB Test + DIM grid (4) as uByte => {0,1,2,3,4} + gridcopy = grid +END SUB diff --git a/tests/functional/arch/zx81sd/arraycopy3.asm b/tests/functional/arch/zx81sd/arraycopy3.asm new file mode 100644 index 000000000..89097c3a6 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy3.asm @@ -0,0 +1,2930 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_Test: + push ix + ld ix, 0 + add ix, sp + ld hl, -12 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 11 + ld d, h + ld e, l + inc de + ldir + ld hl, .LABEL.__LABEL1 + push hl + ld hl, -6 + ld de, .LABEL.__LABEL0 + ld bc, 5 + call .core.__ALLOC_INITIALIZED_LOCAL_ARRAY + ld hl, -12 + ld de, .LABEL.__LABEL2 + ld bc, 5 + call .core.__ALLOC_LOCAL_ARRAY + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-4) + ld h, (ix-3) + push hl + ld hl, 5 + ld b, h + ld c, l + pop hl + pop de + ldir +_Test__leave: + ex af, af' + exx + ld l, (ix-4) + ld h, (ix-3) + call .core.__MEM_FREE + ld l, (ix-10) + ld h, (ix-9) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 58 "arch/zx81sd/arraycopy3.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 59 "arch/zx81sd/arraycopy3.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 01h +.LABEL.__LABEL1: + DEFB 00h + DEFB 01h + DEFB 02h + DEFB 03h + DEFB 04h +.LABEL.__LABEL2: + DEFB 00h + DEFB 00h + DEFB 01h + END diff --git a/tests/functional/arch/zx81sd/arraycopy3.bas b/tests/functional/arch/zx81sd/arraycopy3.bas new file mode 100644 index 000000000..9f273d27d --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy3.bas @@ -0,0 +1,5 @@ +SUB Test + DIM grid (4) as uByte => {0,1,2,3,4} + DIM gridcopy(4) as uByte + gridcopy = grid +END SUB diff --git a/tests/functional/arch/zx81sd/arraycopy4.asm b/tests/functional/arch/zx81sd/arraycopy4.asm new file mode 100644 index 000000000..b8e9b55da --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy4.asm @@ -0,0 +1,3251 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, -12 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 11 + ld d, h + ld e, l + inc de + ldir + ld hl, -6 + ld de, .LABEL.__LABEL0 + ld bc, 22 + call .core.__ALLOC_LOCAL_ARRAY + ld hl, -12 + ld de, .LABEL.__LABEL1 + ld bc, 22 + call .core.__ALLOC_LOCAL_ARRAY + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-4) + ld h, (ix-3) + push hl + ld hl, 11 + push hl + call .core.STR_ARRAYCOPY +_test__leave: + ex af, af' + exx + ld hl, 11 + push hl + ld l, (ix-4) + ld h, (ix-3) + call .core.__ARRAYSTR_FREE_MEM + ld hl, 11 + push hl + ld l, (ix-10) + ld h, (ix-9) + call .core.__ARRAYSTR_FREE_MEM + ex af, af' + exx + ld sp, ix + pop ix + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 58 "arch/zx81sd/arraycopy4.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" + ; This routine is in charge of freeing an array of strings from memory + ; HL = Pointer to start of array in memory + ; Top of the stack = Number of elements of the array +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 6 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" + push namespace core +__ARRAYSTR_FREE: + PROC + LOCAL __ARRAY_LOOP + ex de, hl + pop hl ; (ret address) + ex (sp), hl ; Callee -> HL = Number of elements + ex de, hl +__ARRAYSTR_FREE_FAST: ; Fastcall entry: DE = Number of elements + ld a, h + or l + ret z ; ret if NULL + ld b, d + ld c, e +__ARRAY_LOOP: + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = (HL) = String Pointer + push hl + push bc + ex de, hl + call __MEM_FREE ; Frees it from memory + pop bc + pop hl + dec bc + ld a, b + or c + jp nz, __ARRAY_LOOP + ret ; Frees it and return + ENDP +__ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself + ex de, hl + pop hl ; (ret address) + ex (sp), hl ; Callee -> HL = Number of elements + ex de, hl + push hl ; Saves array pointer for later + call __ARRAYSTR_FREE_FAST + pop hl ; recovers array block pointer + jp __MEM_FREE ; Frees it and returns from __MEM_FREE + pop namespace +#line 59 "arch/zx81sd/arraycopy4.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/strarraycpy.asm" + ; (K)opyleft - by Jose M. Rodriguez de la Rosa (a.k.a. Boriel) + ; 2009 - This is Free OpenSource BSD code +; vim: et:ts=4:sw=4 + ; Copies a vector of strings from one place to another + ; reallocating strings of the destiny vector to hold source strings. +; This is used in the following code: +; DIM a$(20) : DIM b$(20): a$ = b$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/lddede.asm" + ; Loads DE into DE + ; Modifies C register + ; There is a routine similar to this one + ; at ROM address L2AEE + push namespace core +__LOAD_DE_DE: + ex de, hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + ex de, hl + ret + pop namespace +#line 11 "/zxbasic/src/lib/arch/zx48k/runtime/array/strarraycpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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. + ; --------------------------------------------------------------------- + ; 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/zx48k/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 12 "/zxbasic/src/lib/arch/zx48k/runtime/array/strarraycpy.asm" + push namespace core +STR_ARRAYCOPY: + ; Copies an array of string a$ = b$ +; Parameters in the stack: + ; a$, b$, num. of elements; + pop hl ; ret address + pop bc ; num of elements + pop de ; source array + offset to the 1st elem. + ex (sp), hl ; Callee -> hl = destiny array + offset to the 1st elem. + ; FASTCALL ENTRY + ; HL = a$ + offset + ; DE = b$ + offset + ; BC = Number of elements +__STR_ARRAYCOPY: + PROC + LOCAL LOOP +LOOP: + ld a, b + or c + ret z ; Done! + dec bc + push bc + push de + ld a,(hl) + inc hl + ld c,(hl) + dec hl + push hl + ld h, c + ld l, a + call __LOAD_DE_DE + call __STRASSIGN + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + inc hl + pop de + pop bc + inc de + inc de + jp LOOP + ENDP + pop namespace +#line 60 "arch/zx81sd/arraycopy4.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 02h +.LABEL.__LABEL1: + DEFB 00h + DEFB 00h + DEFB 02h + END diff --git a/tests/functional/arch/zx81sd/arraycopy4.bas b/tests/functional/arch/zx81sd/arraycopy4.bas new file mode 100644 index 000000000..d4496fdcb --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy4.bas @@ -0,0 +1,9 @@ +REM test all runtime calls + +SUB test + DIM b$(10) + DIM d$(10) + d = b +END SUB + +test() diff --git a/tests/functional/arch/zx81sd/arraycopy5.asm b/tests/functional/arch/zx81sd/arraycopy5.asm new file mode 100644 index 000000000..c49c120fb --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy5.asm @@ -0,0 +1,3133 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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.__LABEL1 +_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 +.LABEL.__LABEL1: + DEFW 0000h + DEFB 02h +_c: + DEFW .LABEL.__LABEL2 +_c.__DATA__.__PTR__: + DEFW _c.__DATA__ + DEFW 0 + DEFW 0 +_c.__DATA__: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL2: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, .LABEL.__LABEL0 + ld hl, _a.__DATA__ + 2 + call .core.__STORE_STR + ld hl, _c.__DATA__ + push hl + ld hl, _a.__DATA__ + push hl + ld hl, 4 + push hl + call .core.STR_ARRAYCOPY + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +.LABEL.__LABEL0: + DEFW 000Bh + DEFB 48h + DEFB 65h + DEFB 6Ch + DEFB 6Ch + DEFB 6Fh + DEFB 20h + DEFB 77h + DEFB 6Fh + DEFB 72h + DEFB 6Ch + DEFB 64h + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/strarraycpy.asm" + ; (K)opyleft - by Jose M. Rodriguez de la Rosa (a.k.a. Boriel) + ; 2009 - This is Free OpenSource BSD code +; vim: et:ts=4:sw=4 + ; Copies a vector of strings from one place to another + ; reallocating strings of the destiny vector to hold source strings. +; This is used in the following code: +; DIM a$(20) : DIM b$(20): a$ = b$ +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/lddede.asm" + ; Loads DE into DE + ; Modifies C register + ; There is a routine similar to this one + ; at ROM address L2AEE + push namespace core +__LOAD_DE_DE: + ex de, hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + ex de, hl + ret + pop namespace +#line 11 "/zxbasic/src/lib/arch/zx48k/runtime/array/strarraycpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcpy.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 70 "/zxbasic/src/lib/arch/zx48k/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/realloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/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 12 "/zxbasic/src/lib/arch/zx48k/runtime/array/strarraycpy.asm" + push namespace core +STR_ARRAYCOPY: + ; Copies an array of string a$ = b$ +; Parameters in the stack: + ; a$, b$, num. of elements; + pop hl ; ret address + pop bc ; num of elements + pop de ; source array + offset to the 1st elem. + ex (sp), hl ; Callee -> hl = destiny array + offset to the 1st elem. + ; FASTCALL ENTRY + ; HL = a$ + offset + ; DE = b$ + offset + ; BC = Number of elements +__STR_ARRAYCOPY: + PROC + LOCAL LOOP +LOOP: + ld a, b + or c + ret z ; Done! + dec bc + push bc + push de + ld a,(hl) + inc hl + ld c,(hl) + dec hl + push hl + ld h, c + ld l, a + call __LOAD_DE_DE + call __STRASSIGN + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + inc hl + pop de + pop bc + inc de + inc de + jp LOOP + ENDP + pop namespace +#line 32 "arch/zx81sd/arraycopy5.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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$ + 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 33 "arch/zx81sd/arraycopy5.bas" + END diff --git a/tests/functional/arch/zx81sd/arraycopy5.bas b/tests/functional/arch/zx81sd/arraycopy5.bas new file mode 100644 index 000000000..e6e8f0c49 --- /dev/null +++ b/tests/functional/arch/zx81sd/arraycopy5.bas @@ -0,0 +1,5 @@ +DIM a$(3) +DIM c$(3) + +a$(1) = "Hello world" +c$ = a$ diff --git a/tests/functional/arch/zx81sd/bound00.asm b/tests/functional/arch/zx81sd/bound00.asm new file mode 100644 index 000000000..531e9ae5c --- /dev/null +++ b/tests/functional/arch/zx81sd/bound00.asm @@ -0,0 +1,104 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_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 +.LABEL.__LABEL0: + DEFW 0000h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 1 + ld (_b), hl + ld hl, 1 + ld (_c), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/bound00.bas b/tests/functional/arch/zx81sd/bound00.bas new file mode 100644 index 000000000..1824d451a --- /dev/null +++ b/tests/functional/arch/zx81sd/bound00.bas @@ -0,0 +1,5 @@ + +DIM a(2 TO 8) as integer + +LET b = Ubound(a) +LET c = Lbound(a) diff --git a/tests/functional/arch/zx81sd/bound01.asm b/tests/functional/arch/zx81sd/bound01.asm new file mode 100644 index 000000000..47c7c5d62 --- /dev/null +++ b/tests/functional/arch/zx81sd/bound01.asm @@ -0,0 +1,151 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_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 + 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 + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0001h + DEFW 0004h + DEFB 02h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 8 + ld (_b), hl + ld hl, 2 + ld (_c), hl + ld hl, 6 + ld (_b), hl + ld hl, 3 + ld (_c), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/bound01.bas b/tests/functional/arch/zx81sd/bound01.bas new file mode 100644 index 000000000..0ab3a3052 --- /dev/null +++ b/tests/functional/arch/zx81sd/bound01.bas @@ -0,0 +1,7 @@ + +DIM a(2 TO 8, 3 TO 6) as integer + +LET b = Ubound(a, 1) +LET c = Lbound(a, 1) +LET b = Ubound(a, 2) +LET c = Lbound(a, 2) diff --git a/tests/functional/arch/zx81sd/bound02.asm b/tests/functional/arch/zx81sd/bound02.asm new file mode 100644 index 000000000..c3a907df8 --- /dev/null +++ b/tests/functional/arch/zx81sd/bound02.asm @@ -0,0 +1,248 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_c: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW _a.__LBOUND__ + 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 + 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 + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0001h + DEFW 0004h + DEFB 02h +_a.__LBOUND__: + DEFW 0002h + DEFW 0003h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld (_b), a + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__LBOUND + ld (_c), hl + ld a, (_b) + inc a + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__LBOUND + ld (_c), hl + ld a, (_b) + dec a + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__LBOUND + ld (_c), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" + ; --------------------------------------------------------- + ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) +; http://www.boriel.com + ; +; ZX BASIC Compiler http://www.zxbasic.net + ; This code is released under the BSD License + ; --------------------------------------------------------- + ; Implements both LBOUND(array, N) and UBOUND(array, N) function +; Parameters: + ; HL = PTR to array + ; [stack - 2] -> N (dimension) + push namespace core + PROC + LOCAL __BOUND + LOCAL __DIM_NOT_EXIST + LOCAL __CONT +__LBOUND: + ld a, 4 + jr __BOUND +__UBOUND: + ld a, 6 +__BOUND: + ex de, hl ; DE <-- Array ptr + pop hl ; HL <-- Ret address + ex (sp), hl ; CALLEE: HL <-- N, (SP) <-- Ret address + ex de, hl ; DE <-- N, HL <-- ARRAY_PTR + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = start of dimension table (first position contains number of dimensions - 1) + ld c, (hl) + inc hl + ld b, (hl) + inc bc ; Number of total dimensions of the array + pop hl ; Recovers ARRAY PTR + ex af, af' ; Saves A for later + ld a, d + or e + jr nz, __CONT ; N = 0 => Return number of dimensions + ;; Return the number of dimensions of the array + ld h, b + ld l, c + ret +__CONT: + dec de + ex af, af' ; Recovers A (contains PTR offset) + ex de, hl ; HL = N (dimension asked) - 1, DE = Array PTR + or a + sbc hl, bc ; if no Carry => the user asked for a dimension that does not exist. Return 0 + jr nc, __DIM_NOT_EXIST + add hl, bc ; restores HL = (N - 1) + add hl, hl ; hl *= 2 + ex de, hl ; hl = ARRAY_PTR + 3, DE jsz = (N - 1) * 2 + ld b, 0 + ld c, a + add hl, bc ; HL = &BOUND_PTR + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; LD HL, (HL) => Origin of L/U Bound table + ; for LBound only, HL = 0x0000 (NULL) if the array is all 0-based + or h + ret z ; Should never happen for UBound + add hl, de ; hl += OFFSET __LBOUND._xxxx + ld e, (hl) ; de = (hl) + inc hl + ld d, (hl) + ex de, hl ; hl = de => returns result in HL + ret +__DIM_NOT_EXIST: + ; The dimension requested by the user does not exists. Return 0 + ld hl, 0 + ret + ENDP + pop namespace +#line 33 "arch/zx81sd/bound02.bas" + END diff --git a/tests/functional/arch/zx81sd/bound02.bas b/tests/functional/arch/zx81sd/bound02.bas new file mode 100644 index 000000000..7a46f2a16 --- /dev/null +++ b/tests/functional/arch/zx81sd/bound02.bas @@ -0,0 +1,7 @@ + +DIM a(2 TO 8, 3 TO 6) as integer + +LET b = 1 +LET c = Lbound(a, b) +LET c = Lbound(a, b + 1) +LET c = Lbound(a, b - 1) diff --git a/tests/functional/arch/zx81sd/bound03.asm b/tests/functional/arch/zx81sd/bound03.asm new file mode 100644 index 000000000..1fc9b497e --- /dev/null +++ b/tests/functional/arch/zx81sd/bound03.asm @@ -0,0 +1,248 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_c: + DEFB 00, 00 +_a: + DEFW .LABEL.__LABEL0 +_a.__DATA__.__PTR__: + DEFW _a.__DATA__ + DEFW 0 + DEFW _a.__UBOUND__ +_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 + 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 + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL0: + DEFW 0001h + DEFW 0004h + DEFB 02h +_a.__UBOUND__: + DEFW 0008h + DEFW 0006h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld (_b), a + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__UBOUND + ld (_c), hl + ld a, (_b) + inc a + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__UBOUND + ld (_c), hl + ld a, (_b) + dec a + ld l, a + ld h, 0 + push hl + ld hl, _a + call .core.__UBOUND + ld (_c), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" + ; --------------------------------------------------------- + ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) +; http://www.boriel.com + ; +; ZX BASIC Compiler http://www.zxbasic.net + ; This code is released under the BSD License + ; --------------------------------------------------------- + ; Implements both LBOUND(array, N) and UBOUND(array, N) function +; Parameters: + ; HL = PTR to array + ; [stack - 2] -> N (dimension) + push namespace core + PROC + LOCAL __BOUND + LOCAL __DIM_NOT_EXIST + LOCAL __CONT +__LBOUND: + ld a, 4 + jr __BOUND +__UBOUND: + ld a, 6 +__BOUND: + ex de, hl ; DE <-- Array ptr + pop hl ; HL <-- Ret address + ex (sp), hl ; CALLEE: HL <-- N, (SP) <-- Ret address + ex de, hl ; DE <-- N, HL <-- ARRAY_PTR + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = start of dimension table (first position contains number of dimensions - 1) + ld c, (hl) + inc hl + ld b, (hl) + inc bc ; Number of total dimensions of the array + pop hl ; Recovers ARRAY PTR + ex af, af' ; Saves A for later + ld a, d + or e + jr nz, __CONT ; N = 0 => Return number of dimensions + ;; Return the number of dimensions of the array + ld h, b + ld l, c + ret +__CONT: + dec de + ex af, af' ; Recovers A (contains PTR offset) + ex de, hl ; HL = N (dimension asked) - 1, DE = Array PTR + or a + sbc hl, bc ; if no Carry => the user asked for a dimension that does not exist. Return 0 + jr nc, __DIM_NOT_EXIST + add hl, bc ; restores HL = (N - 1) + add hl, hl ; hl *= 2 + ex de, hl ; hl = ARRAY_PTR + 3, DE jsz = (N - 1) * 2 + ld b, 0 + ld c, a + add hl, bc ; HL = &BOUND_PTR + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; LD HL, (HL) => Origin of L/U Bound table + ; for LBound only, HL = 0x0000 (NULL) if the array is all 0-based + or h + ret z ; Should never happen for UBound + add hl, de ; hl += OFFSET __LBOUND._xxxx + ld e, (hl) ; de = (hl) + inc hl + ld d, (hl) + ex de, hl ; hl = de => returns result in HL + ret +__DIM_NOT_EXIST: + ; The dimension requested by the user does not exists. Return 0 + ld hl, 0 + ret + ENDP + pop namespace +#line 33 "arch/zx81sd/bound03.bas" + END diff --git a/tests/functional/arch/zx81sd/bound03.bas b/tests/functional/arch/zx81sd/bound03.bas new file mode 100644 index 000000000..9ea251e2b --- /dev/null +++ b/tests/functional/arch/zx81sd/bound03.bas @@ -0,0 +1,8 @@ + +DIM a(2 TO 8, 3 TO 6) as integer + +LET b = 1 + +LET c = Ubound(a, b) +LET c = Ubound(a, b + 1) +LET c = UBound(a, b-1) diff --git a/tests/functional/arch/zx81sd/bound04.asm b/tests/functional/arch/zx81sd/bound04.asm new file mode 100644 index 000000000..909aa30d2 --- /dev/null +++ b/tests/functional/arch/zx81sd/bound04.asm @@ -0,0 +1,131 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_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 + 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 0007h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 2 + ld (_c), hl + ld hl, 2 + ld (_c), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/bound04.bas b/tests/functional/arch/zx81sd/bound04.bas new file mode 100644 index 000000000..98626adcc --- /dev/null +++ b/tests/functional/arch/zx81sd/bound04.bas @@ -0,0 +1,6 @@ +REM Constant dimensions test + +DIM a(5, 6) as Byte + +c = LBound(a, 0) +c = UBound(a, 0) diff --git a/tests/functional/arch/zx81sd/cast_const_to_int.asm b/tests/functional/arch/zx81sd/cast_const_to_int.asm new file mode 100644 index 000000000..0e4c0884f --- /dev/null +++ b/tests/functional/arch/zx81sd/cast_const_to_int.asm @@ -0,0 +1,268 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_var_neg: + DEFB 00h + DEFB 80h + DEFB 0FEh + DEFB 0FFh +_var: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_var_neg_fl: + DEFB 81h + DEFB 0C0h + DEFB 00h + DEFB 00h + DEFB 00h +_r_int: + DEFB 00, 00 +_r_long: + DEFB 00, 00, 00, 00 +_r_ulong: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld (_r_int), hl + ld hl, 65534 + ld (_r_int), hl + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var_neg) + ld de, (_var_neg + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld (_r_int), hl + ld hl, 1 + ld (_r_int), hl + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_long), hl + ld (_r_long + 2), de + ld hl, (_var) + ld de, (_var + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_int), hl + ld hl, 65534 + ld (_r_int), hl + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_long), hl + ld (_r_long + 2), de + ld a, (_var_neg_fl) + ld de, (_var_neg_fl + 1) + ld bc, (_var_neg_fl + 3) + call .core.__FTOU32REG + ld (_r_ulong), hl + ld (_r_ulong + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#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 + 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/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 84 "arch/zx81sd/cast_const_to_int.bas" + END diff --git a/tests/functional/arch/zx81sd/cast_const_to_int.bas b/tests/functional/arch/zx81sd/cast_const_to_int.bas new file mode 100644 index 000000000..25d7b2b04 --- /dev/null +++ b/tests/functional/arch/zx81sd/cast_const_to_int.bas @@ -0,0 +1,25 @@ +dim var_neg as fixed = -1.5 +const const_neg as fixed = -1.5 +dim var as Fixed = 1.5 +const const_ as fixed = 1.5 +dim var_neg_fl as Float = -1.5 +const const_neg_fl as Float = -1.5 + +DIM r_int as Integer +DIM r_long as Long +DIM r_ulong as ULong + +LET r_int = cast(integer, var_neg) +LET r_int = cast(integer, const_neg) +LET r_long = cast(long, var_neg) +LET r_ulong = cast(ulong, var_neg) + +LET r_int = cast(integer, var) +LET r_int = cast(integer, const_) +LET r_long = cast(long, var) +LET r_ulong = cast(ulong, var) + +LET r_int = cast(integer, var_neg_fl) +LET r_int = cast(integer, const_neg_fl) +LET r_long = cast(long, var_neg_fl) +LET r_ulong = cast(ulong, var_neg_fl) diff --git a/tests/functional/arch/zx81sd/cast_f16_to_long.asm b/tests/functional/arch/zx81sd/cast_f16_to_long.asm new file mode 100644 index 000000000..f19c9fb24 --- /dev/null +++ b/tests/functional/arch/zx81sd/cast_f16_to_long.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_v1: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_v2: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_v1) + ld de, (_v1 + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_v2), hl + ld (_v2 + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/cast_f16_to_long.bas b/tests/functional/arch/zx81sd/cast_f16_to_long.bas new file mode 100644 index 000000000..fe2813338 --- /dev/null +++ b/tests/functional/arch/zx81sd/cast_f16_to_long.bas @@ -0,0 +1,4 @@ +DIM v1 As Fixed = 1.5 +DIM v2 As Long + +v2 = v1 diff --git a/tests/functional/arch/zx81sd/cast_f16_to_ulong.asm b/tests/functional/arch/zx81sd/cast_f16_to_ulong.asm new file mode 100644 index 000000000..f19c9fb24 --- /dev/null +++ b/tests/functional/arch/zx81sd/cast_f16_to_ulong.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_v1: + DEFB 00h + DEFB 80h + DEFB 01h + DEFB 00h +_v2: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_v1) + ld de, (_v1 + 2) + ex de, hl + ld de, 0 + ld a, h + add a, a + sbc a, a + ld e, a + ld d, a + ld (_v2), hl + ld (_v2 + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/cast_f16_to_ulong.bas b/tests/functional/arch/zx81sd/cast_f16_to_ulong.bas new file mode 100644 index 000000000..bb523096e --- /dev/null +++ b/tests/functional/arch/zx81sd/cast_f16_to_ulong.bas @@ -0,0 +1,4 @@ +DIM v1 As Fixed = 1.5 +DIM v2 As ULong + +v2 = v1 diff --git a/tests/functional/arch/zx81sd/div32.asm b/tests/functional/arch/zx81sd/div32.asm new file mode 100644 index 000000000..7ba8ab620 --- /dev/null +++ b/tests/functional/arch/zx81sd/div32.asm @@ -0,0 +1,2526 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/negf.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 + 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/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/zx48k/runtime/negf.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) + ; 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/zx48k/runtime/negf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 4 "/zxbasic/src/lib/arch/zx48k/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 19 "arch/zx81sd/div32.bas" + END diff --git a/tests/functional/arch/zx81sd/div32.bas b/tests/functional/arch/zx81sd/div32.bas new file mode 100644 index 000000000..ead0b2565 --- /dev/null +++ b/tests/functional/arch/zx81sd/div32.bas @@ -0,0 +1,3 @@ +DIM a as Fixed + +LET a = a / -1 diff --git a/tests/functional/arch/zx81sd/divf00.asm b/tests/functional/arch/zx81sd/divf00.asm new file mode 100644 index 000000000..5a98a771f --- /dev/null +++ b/tests/functional/arch/zx81sd/divf00.asm @@ -0,0 +1,2460 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx81sd/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 + ; ------------------------------------------------------------- + ; +; zx81sd override: the only change from zx48k's version is where + ; TMP/ERR_SP live. The original uses the Spectrum ROM's DEST (23629) + ; and ERR_SP (23613) system variables to save/restore a stack recovery + ; point around the division (a "longjmp" trick for divide-by-zero) -- + ; on zx81sd that mechanism never actually gets used for real recovery + ; (__ERROR does DI+HALT directly, it doesn't restore SP from ERR_SP), + ; but this code writes there on every division regardless of whether + ; an error happens, and those addresses fall inside the program's own + ; compiled code (block 2) instead of real sysvars. DIVF_SCRATCH + ; (sysvars.asm) is dedicated scratch space instead -- same mechanism, + ; just relocated. + push namespace core +__DIVF: ; Division + PROC + LOCAL __DIVBYZERO + LOCAL TMP, ERR_SP +TMP EQU DIVF_SCRATCH ; zx81sd: dedicated scratch, not DEST +ERR_SP EQU DIVF_SCRATCH + 2 ; zx81sd: dedicated scratch, not ERR_SP + call __FPSTACK_PUSH2 + ld hl, (ERR_SP) + ld (TMP), hl + ld hl, __DIVBYZERO + push hl + ld (ERR_SP), sp + ; ------------- 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 17 "arch/zx81sd/divf00.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 18 "arch/zx81sd/divf00.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 19 "arch/zx81sd/divf00.bas" + END diff --git a/tests/functional/arch/zx81sd/divf00.bas b/tests/functional/arch/zx81sd/divf00.bas new file mode 100644 index 000000000..9b6450fd8 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divf01.asm b/tests/functional/arch/zx81sd/divf01.asm new file mode 100644 index 000000000..0ace89154 --- /dev/null +++ b/tests/functional/arch/zx81sd/divf01.asm @@ -0,0 +1,2436 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/arith/divf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx81sd/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 + ; ------------------------------------------------------------- + ; +; zx81sd override: the only change from zx48k's version is where + ; TMP/ERR_SP live. The original uses the Spectrum ROM's DEST (23629) + ; and ERR_SP (23613) system variables to save/restore a stack recovery + ; point around the division (a "longjmp" trick for divide-by-zero) -- + ; on zx81sd that mechanism never actually gets used for real recovery + ; (__ERROR does DI+HALT directly, it doesn't restore SP from ERR_SP), + ; but this code writes there on every division regardless of whether + ; an error happens, and those addresses fall inside the program's own + ; compiled code (block 2) instead of real sysvars. DIVF_SCRATCH + ; (sysvars.asm) is dedicated scratch space instead -- same mechanism, + ; just relocated. + push namespace core +__DIVF: ; Division + PROC + LOCAL __DIVBYZERO + LOCAL TMP, ERR_SP +TMP EQU DIVF_SCRATCH ; zx81sd: dedicated scratch, not DEST +ERR_SP EQU DIVF_SCRATCH + 2 ; zx81sd: dedicated scratch, not ERR_SP + call __FPSTACK_PUSH2 + ld hl, (ERR_SP) + ld (TMP), hl + ld hl, __DIVBYZERO + push hl + ld (ERR_SP), sp + ; ------------- 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/zx81sd/divf01.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 22 "arch/zx81sd/divf01.bas" + END diff --git a/tests/functional/arch/zx81sd/divf01.bas b/tests/functional/arch/zx81sd/divf01.bas new file mode 100644 index 000000000..fb917f97d --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divf16.asm b/tests/functional/arch/zx81sd/divf16.asm new file mode 100644 index 000000000..ed3a11bda --- /dev/null +++ b/tests/functional/arch/zx81sd/divf16.asm @@ -0,0 +1,354 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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/zx48k/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 76 "arch/zx81sd/divf16.bas" + END diff --git a/tests/functional/arch/zx81sd/divf16.bas b/tests/functional/arch/zx81sd/divf16.bas new file mode 100644 index 000000000..3fa4dba4a --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divf16a.asm b/tests/functional/arch/zx81sd/divf16a.asm new file mode 100644 index 000000000..86c2c78c6 --- /dev/null +++ b/tests/functional/arch/zx81sd/divf16a.asm @@ -0,0 +1,299 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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/zx48k/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 23 "arch/zx81sd/divf16a.bas" + END diff --git a/tests/functional/arch/zx81sd/divf16a.bas b/tests/functional/arch/zx81sd/divf16a.bas new file mode 100644 index 000000000..5ca0bd882 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divf16b.asm b/tests/functional/arch/zx81sd/divf16b.asm new file mode 100644 index 000000000..81ae4638d --- /dev/null +++ b/tests/functional/arch/zx81sd/divf16b.asm @@ -0,0 +1,309 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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/zx48k/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 33 "arch/zx81sd/divf16b.bas" + END diff --git a/tests/functional/arch/zx81sd/divf16b.bas b/tests/functional/arch/zx81sd/divf16b.bas new file mode 100644 index 000000000..601774854 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divf16c.asm b/tests/functional/arch/zx81sd/divf16c.asm new file mode 100644 index 000000000..ab8b8ccd8 --- /dev/null +++ b/tests/functional/arch/zx81sd/divf16c.asm @@ -0,0 +1,359 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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/zx48k/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 55 "arch/zx81sd/divf16c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 56 "arch/zx81sd/divf16c.bas" + END diff --git a/tests/functional/arch/zx81sd/divf16c.bas b/tests/functional/arch/zx81sd/divf16c.bas new file mode 100644 index 000000000..642235083 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divi16a.asm b/tests/functional/arch/zx81sd/divi16a.asm new file mode 100644 index 000000000..641eb7afb --- /dev/null +++ b/tests/functional/arch/zx81sd/divi16a.asm @@ -0,0 +1,173 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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" + ; 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/zx48k/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 15 "arch/zx81sd/divi16a.bas" + END diff --git a/tests/functional/arch/zx81sd/divi16a.bas b/tests/functional/arch/zx81sd/divi16a.bas new file mode 100644 index 000000000..4a4d179d5 --- /dev/null +++ b/tests/functional/arch/zx81sd/divi16a.bas @@ -0,0 +1,4 @@ + +REM another DIV16 test +DIM a As UInteger +a = a / a / a diff --git a/tests/functional/arch/zx81sd/divi16b.asm b/tests/functional/arch/zx81sd/divi16b.asm new file mode 100644 index 000000000..8cf401290 --- /dev/null +++ b/tests/functional/arch/zx81sd/divi16b.asm @@ -0,0 +1,180 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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" + ; 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/zx48k/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 22 "arch/zx81sd/divi16b.bas" + END diff --git a/tests/functional/arch/zx81sd/divi16b.bas b/tests/functional/arch/zx81sd/divi16b.bas new file mode 100644 index 000000000..34d78b02f --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divi32c.asm b/tests/functional/arch/zx81sd/divi32c.asm new file mode 100644 index 000000000..504f10ff0 --- /dev/null +++ b/tests/functional/arch/zx81sd/divi32c.asm @@ -0,0 +1,291 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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 58 "arch/zx81sd/divi32c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 59 "arch/zx81sd/divi32c.bas" + END diff --git a/tests/functional/arch/zx81sd/divi32c.bas b/tests/functional/arch/zx81sd/divi32c.bas new file mode 100644 index 000000000..714b198d5 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divi8.asm b/tests/functional/arch/zx81sd/divi8.asm new file mode 100644 index 000000000..f53316b0f --- /dev/null +++ b/tests/functional/arch/zx81sd/divi8.asm @@ -0,0 +1,169 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 36 "arch/zx81sd/divi8.bas" + END diff --git a/tests/functional/arch/zx81sd/divi8.bas b/tests/functional/arch/zx81sd/divi8.bas new file mode 100644 index 000000000..6d0fa4935 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divi8a.asm b/tests/functional/arch/zx81sd/divi8a.asm new file mode 100644 index 000000000..9dcdeb069 --- /dev/null +++ b/tests/functional/arch/zx81sd/divi8a.asm @@ -0,0 +1,146 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 15 "arch/zx81sd/divi8a.bas" + END diff --git a/tests/functional/arch/zx81sd/divi8a.bas b/tests/functional/arch/zx81sd/divi8a.bas new file mode 100644 index 000000000..0e2d69aa5 --- /dev/null +++ b/tests/functional/arch/zx81sd/divi8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a / a / a diff --git a/tests/functional/arch/zx81sd/divi8b.asm b/tests/functional/arch/zx81sd/divi8b.asm new file mode 100644 index 000000000..687575ca2 --- /dev/null +++ b/tests/functional/arch/zx81sd/divi8b.asm @@ -0,0 +1,152 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 21 "arch/zx81sd/divi8b.bas" + END diff --git a/tests/functional/arch/zx81sd/divi8b.bas b/tests/functional/arch/zx81sd/divi8b.bas new file mode 100644 index 000000000..17ea37bf9 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divu16.asm b/tests/functional/arch/zx81sd/divu16.asm new file mode 100644 index 000000000..6090bdc02 --- /dev/null +++ b/tests/functional/arch/zx81sd/divu16.asm @@ -0,0 +1,197 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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" + ; 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/zx48k/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 37 "arch/zx81sd/divu16.bas" + END diff --git a/tests/functional/arch/zx81sd/divu16.bas b/tests/functional/arch/zx81sd/divu16.bas new file mode 100644 index 000000000..dc3395036 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divu16a.asm b/tests/functional/arch/zx81sd/divu16a.asm new file mode 100644 index 000000000..cd919f665 --- /dev/null +++ b/tests/functional/arch/zx81sd/divu16a.asm @@ -0,0 +1,173 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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" + ; 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/zx48k/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 15 "arch/zx81sd/divu16a.bas" + END diff --git a/tests/functional/arch/zx81sd/divu16a.bas b/tests/functional/arch/zx81sd/divu16a.bas new file mode 100644 index 000000000..6dc894b38 --- /dev/null +++ b/tests/functional/arch/zx81sd/divu16a.bas @@ -0,0 +1,4 @@ + +REM another DIVU16 test +DIM a As UInteger +a = a / a / a diff --git a/tests/functional/arch/zx81sd/divu16b.asm b/tests/functional/arch/zx81sd/divu16b.asm new file mode 100644 index 000000000..29ef23ca2 --- /dev/null +++ b/tests/functional/arch/zx81sd/divu16b.asm @@ -0,0 +1,180 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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" + ; 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/zx48k/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 22 "arch/zx81sd/divu16b.bas" + END diff --git a/tests/functional/arch/zx81sd/divu16b.bas b/tests/functional/arch/zx81sd/divu16b.bas new file mode 100644 index 000000000..309f7fb45 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divu32c.asm b/tests/functional/arch/zx81sd/divu32c.asm new file mode 100644 index 000000000..9b351017c --- /dev/null +++ b/tests/functional/arch/zx81sd/divu32c.asm @@ -0,0 +1,291 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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 58 "arch/zx81sd/divu32c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 59 "arch/zx81sd/divu32c.bas" + END diff --git a/tests/functional/arch/zx81sd/divu32c.bas b/tests/functional/arch/zx81sd/divu32c.bas new file mode 100644 index 000000000..87cada97c --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divu8.asm b/tests/functional/arch/zx81sd/divu8.asm new file mode 100644 index 000000000..638293924 --- /dev/null +++ b/tests/functional/arch/zx81sd/divu8.asm @@ -0,0 +1,169 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 36 "arch/zx81sd/divu8.bas" + END diff --git a/tests/functional/arch/zx81sd/divu8.bas b/tests/functional/arch/zx81sd/divu8.bas new file mode 100644 index 000000000..a8526bc53 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/divu8a.asm b/tests/functional/arch/zx81sd/divu8a.asm new file mode 100644 index 000000000..59de23eeb --- /dev/null +++ b/tests/functional/arch/zx81sd/divu8a.asm @@ -0,0 +1,146 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 15 "arch/zx81sd/divu8a.bas" + END diff --git a/tests/functional/arch/zx81sd/divu8a.bas b/tests/functional/arch/zx81sd/divu8a.bas new file mode 100644 index 000000000..532f027cc --- /dev/null +++ b/tests/functional/arch/zx81sd/divu8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As UByte +a = a / a / a diff --git a/tests/functional/arch/zx81sd/divu8b.asm b/tests/functional/arch/zx81sd/divu8b.asm new file mode 100644 index 000000000..83169a4c9 --- /dev/null +++ b/tests/functional/arch/zx81sd/divu8b.asm @@ -0,0 +1,152 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 21 "arch/zx81sd/divu8b.bas" + END diff --git a/tests/functional/arch/zx81sd/divu8b.bas b/tests/functional/arch/zx81sd/divu8b.bas new file mode 100644 index 000000000..bdae6477f --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/equ16.asm b/tests/functional/arch/zx81sd/equ16.asm new file mode 100644 index 000000000..8893b6fe8 --- /dev/null +++ b/tests/functional/arch/zx81sd/equ16.asm @@ -0,0 +1,94 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, 5 + ld hl, (_a) + call .core.__EQ16 + or a + jp z, .LABEL.__LABEL1 + ld hl, (_a) + inc hl + ld (_a), hl +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/eq16.asm" + push namespace core +__EQ16: ; Test if 16bit values HL == DE + ; Returns result in A: 0 = False, FF = True + xor a ; Reset carry flag + sbc hl, de + ret nz + inc a + ret + pop namespace +#line 18 "arch/zx81sd/equ16.bas" + END diff --git a/tests/functional/arch/zx81sd/equ16.bas b/tests/functional/arch/zx81sd/equ16.bas new file mode 100644 index 000000000..a00f0ea69 --- /dev/null +++ b/tests/functional/arch/zx81sd/equ16.bas @@ -0,0 +1,5 @@ +DIM a as UInteger + +IF a = 5 THEN + LET a = a + 1 +END IF diff --git a/tests/functional/arch/zx81sd/equ32.asm b/tests/functional/arch/zx81sd/equ32.asm new file mode 100644 index 000000000..4a49e677d --- /dev/null +++ b/tests/functional/arch/zx81sd/equ32.asm @@ -0,0 +1,120 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 de, 0 + ld hl, 5 + call .core.__EQ32 + or a + jp z, .LABEL.__LABEL1 + 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 (_a), hl + ld (_a + 2), de +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/eq32.asm" + push namespace core +__EQ32: ; Test if 32bit value HLDE equals top of the stack + ; Returns result in A: 0 = False, FF = True + exx + pop bc ; Return address + exx + xor a ; Reset carry flag + pop bc + sbc hl, bc ; Low part + ex de, hl + pop bc + sbc hl, bc ; High part + exx + push bc ; CALLEE + exx + ld a, h + or l + or d + or e ; a = 0 and Z flag set only if HLDE = 0 + ld a, 1 + ret z + xor a + ret + pop namespace +#line 29 "arch/zx81sd/equ32.bas" + END diff --git a/tests/functional/arch/zx81sd/equ32.bas b/tests/functional/arch/zx81sd/equ32.bas new file mode 100644 index 000000000..b9a67da54 --- /dev/null +++ b/tests/functional/arch/zx81sd/equ32.bas @@ -0,0 +1,5 @@ +DIM a as ULong + +IF a = 5 THEN + LET a = a + 1 +END IF diff --git a/tests/functional/arch/zx81sd/equ8.asm b/tests/functional/arch/zx81sd/equ8.asm new file mode 100644 index 000000000..c3bc57508 --- /dev/null +++ b/tests/functional/arch/zx81sd/equ8.asm @@ -0,0 +1,80 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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) + sub 5 + jp nz, .LABEL.__LABEL1 + ld hl, _a + inc (hl) +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/equ8.bas b/tests/functional/arch/zx81sd/equ8.bas new file mode 100644 index 000000000..2491f0b86 --- /dev/null +++ b/tests/functional/arch/zx81sd/equ8.bas @@ -0,0 +1,5 @@ +DIM a as UByte + +IF a = 5 THEN + LET a = a + 1 +END IF diff --git a/tests/functional/arch/zx81sd/equf.asm b/tests/functional/arch/zx81sd/equf.asm new file mode 100644 index 000000000..8ba5a182c --- /dev/null +++ b/tests/functional/arch/zx81sd/equf.asm @@ -0,0 +1,2611 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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, 083h + ld de, 00020h + ld bc, 00000h + call .core.__EQF + or a + jp z, .LABEL.__LABEL1 + ld hl, _a + 4 + call .core.__FP_PUSH_REV + ld a, 081h + ld de, 00000h + ld bc, 00000h + call .core.__ADDF + ld hl, _a + call .core.__STOREF +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/addf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#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. + ; 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 26 "arch/zx81sd/equf.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 + 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/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/zx48k/runtime/cmp/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) + ; 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/zx48k/runtime/cmp/eqf.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 +__EQF: ; A = B + call __FPSTACK_PUSH2 + ; ------------- ROM NOS-EQL + ld b, 0Eh ; For comparison operators, OP must be in B also + rst 28h + defb 0Eh + defb 38h; ; END CALC + call __FPSTACK_POP + jp __FTOU8 ; Convert to 8 bits + pop namespace +#line 27 "arch/zx81sd/equf.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 28 "arch/zx81sd/equf.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 29 "arch/zx81sd/equf.bas" + END diff --git a/tests/functional/arch/zx81sd/equf.bas b/tests/functional/arch/zx81sd/equf.bas new file mode 100644 index 000000000..c44bc7988 --- /dev/null +++ b/tests/functional/arch/zx81sd/equf.bas @@ -0,0 +1,5 @@ +DIM a as Float + +IF a = 5 THEN + LET a = a + 1 +END IF diff --git a/tests/functional/arch/zx81sd/fact.asm b/tests/functional/arch/zx81sd/fact.asm new file mode 100644 index 000000000..cf27e79f8 --- /dev/null +++ b/tests/functional/arch/zx81sd/fact.asm @@ -0,0 +1,252 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 00, 00, 00, 00 +_x: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld (_x), a + jp .LABEL.__LABEL0 +.LABEL.__LABEL3: + ld a, (_x) + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_y), hl + ld (_y + 2), de +.LABEL.__LABEL4: + ld hl, _x + inc (hl) +.LABEL.__LABEL0: + ld a, 10 + ld hl, (_x - 1) + cp h + jp nc, .LABEL.__LABEL3 +.LABEL.__LABEL2: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_fact: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld de, 0 + ld hl, 2 + call .core.__SUB32 + jp nc, .LABEL.__LABEL6 + ld de, 0 + ld hl, 1 + jp _fact__leave +.LABEL.__LABEL6: + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld l, (ix+4) + ld h, (ix+5) + ld e, (ix+6) + ld d, (ix+7) + push de + push hl + ld de, 0 + ld hl, 1 + call .core.__SUB32 + push de + push hl + call _fact + call .core.__MUL32 +_fact__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret + ;; --- 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 +#line 75 "arch/zx81sd/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 76 "arch/zx81sd/fact.bas" + END diff --git a/tests/functional/arch/zx81sd/fact.bas b/tests/functional/arch/zx81sd/fact.bas new file mode 100644 index 000000000..80cf0e1d6 --- /dev/null +++ b/tests/functional/arch/zx81sd/fact.bas @@ -0,0 +1,14 @@ +REM Factorial recursive test +DIM y as ulong + +function fact(x as ulong) as ulong + if x < 2 then + return 1 + end if + + return x * fact(x - 1) +end function + +for x = 1 To 10: + let y = x: rem dummy +next x diff --git a/tests/functional/arch/zx81sd/fp_pow.asm b/tests/functional/arch/zx81sd/fp_pow.asm new file mode 100644 index 000000000..471a4505e --- /dev/null +++ b/tests/functional/arch/zx81sd/fp_pow.asm @@ -0,0 +1,2418 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _x + 4 + call .core.__FP_PUSH_REV + ld a, 082h + ld de, 00020h + ld bc, 00000h + call .core.__POW + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/pow.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/pow.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 + ; +; Operands comes swapped: + ; 1 st parameter is the BASE (A ED CB) + ; 2 nd parameter (Top of the stack) is Exponent + ; ------------------------------------------------------------- + push namespace core +__POW: ; Exponentiation + PROC + call __FPSTACK_PUSH2 + ; ------------- ROM POW + rst 28h + defb 01h ; Exchange => 1, Base + defb 06h ; POW + defb 38h; ; END CALC + jp __FPSTACK_POP + ENDP + pop namespace +#line 17 "arch/zx81sd/fp_pow.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 18 "arch/zx81sd/fp_pow.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 19 "arch/zx81sd/fp_pow.bas" + END diff --git a/tests/functional/arch/zx81sd/fp_pow.bas b/tests/functional/arch/zx81sd/fp_pow.bas new file mode 100644 index 000000000..1bb07f8f9 --- /dev/null +++ b/tests/functional/arch/zx81sd/fp_pow.bas @@ -0,0 +1,3 @@ +DIM x as Float + +LET x = x ^ 2.5 diff --git a/tests/functional/arch/zx81sd/gef.asm b/tests/functional/arch/zx81sd/gef.asm new file mode 100644 index 000000000..cd4763063 --- /dev/null +++ b/tests/functional/arch/zx81sd/gef.asm @@ -0,0 +1,2644 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h + DEFB 00h +_le: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__GEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__GEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__GEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__GEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, 00000h + push hl + ld hl, 00000h + push hl + ld h, 081h + push hl + call .core.__GEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/gef.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 + 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/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/zx48k/runtime/cmp/gef.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) + ; 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/zx48k/runtime/cmp/gef.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/gef.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 +__GEF: ; A >= B + call __FPSTACK_PUSH2 ; Enters B, A + ; ------------- ROM NO-LESS + ld b, 09h ; B =< A + rst 28h + defb 09h + defb 38h ; END CALC + call __FPSTACK_POP + jp __FTOU8 ; Convert to 8 bits + pop namespace +#line 66 "arch/zx81sd/gef.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 67 "arch/zx81sd/gef.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 68 "arch/zx81sd/gef.bas" + END diff --git a/tests/functional/arch/zx81sd/gef.bas b/tests/functional/arch/zx81sd/gef.bas new file mode 100644 index 000000000..f6c6d0ded --- /dev/null +++ b/tests/functional/arch/zx81sd/gef.bas @@ -0,0 +1,9 @@ +dim level as Float = 0 +dim le as Float = 1 +dim l as Float + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) +l = 1 >= (level + 0) diff --git a/tests/functional/arch/zx81sd/gef16.asm b/tests/functional/arch/zx81sd/gef16.asm new file mode 100644 index 000000000..c6e59131c --- /dev/null +++ b/tests/functional/arch/zx81sd/gef16.asm @@ -0,0 +1,217 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 01h + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 74 "arch/zx81sd/gef16.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 75 "arch/zx81sd/gef16.bas" + END diff --git a/tests/functional/arch/zx81sd/gef16.bas b/tests/functional/arch/zx81sd/gef16.bas new file mode 100644 index 000000000..8a7212d29 --- /dev/null +++ b/tests/functional/arch/zx81sd/gef16.bas @@ -0,0 +1,8 @@ +dim level as fixed = 0 +dim le as fixed = 1 +dim l as fixed + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) diff --git a/tests/functional/arch/zx81sd/gei16.asm b/tests/functional/arch/zx81sd/gei16.asm new file mode 100644 index 000000000..8b060d37e --- /dev/null +++ b/tests/functional/arch/zx81sd/gei16.asm @@ -0,0 +1,130 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 01h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_le) + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ld de, (_le) + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_le) + ld hl, (_level) + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_le) + ld hl, (_level) + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ld de, 1 + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lei16.asm" + push namespace core +__LEI16: + PROC + LOCAL checkParity + or a + sbc hl, de + ld a, 1 + ret z + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 39 "arch/zx81sd/gei16.bas" + END diff --git a/tests/functional/arch/zx81sd/gei16.bas b/tests/functional/arch/zx81sd/gei16.bas new file mode 100644 index 000000000..a98ecdb9b --- /dev/null +++ b/tests/functional/arch/zx81sd/gei16.bas @@ -0,0 +1,9 @@ +dim level as Integer = 0 +dim le as Integer = 1 +dim l as Integer + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) +l = 1 >= (level + 0) diff --git a/tests/functional/arch/zx81sd/gei32.asm b/tests/functional/arch/zx81sd/gei32.asm new file mode 100644 index 000000000..77f677e32 --- /dev/null +++ b/tests/functional/arch/zx81sd/gei32.asm @@ -0,0 +1,233 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h +_le: + DEFB 01h + 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 + sub 1 + sbc a, a + neg + 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 + sub 1 + sbc a, a + neg + 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 + sub 1 + sbc a, a + neg + 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 + sub 1 + sbc a, a + neg + 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, 1 + push bc + call .core.__LTI32 + sub 1 + sbc a, a + neg + 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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 90 "arch/zx81sd/gei32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 91 "arch/zx81sd/gei32.bas" + END diff --git a/tests/functional/arch/zx81sd/gei32.bas b/tests/functional/arch/zx81sd/gei32.bas new file mode 100644 index 000000000..7e7ed2386 --- /dev/null +++ b/tests/functional/arch/zx81sd/gei32.bas @@ -0,0 +1,9 @@ +dim level as long = 0 +dim le as long = 1 +dim l as long + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) +l = 1 >= (level + 0) diff --git a/tests/functional/arch/zx81sd/gei8.asm b/tests/functional/arch/zx81sd/gei8.asm new file mode 100644 index 000000000..e1e87460a --- /dev/null +++ b/tests/functional/arch/zx81sd/gei8.asm @@ -0,0 +1,105 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, 5 + ld hl, (_a - 1) + call .core.__LEI8 + or a + jp z, .LABEL.__LABEL1 + ld a, 255 + ld (16384), a +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 17 "arch/zx81sd/gei8.bas" + END diff --git a/tests/functional/arch/zx81sd/gei8.bas b/tests/functional/arch/zx81sd/gei8.bas new file mode 100644 index 000000000..a71b5632e --- /dev/null +++ b/tests/functional/arch/zx81sd/gei8.bas @@ -0,0 +1,5 @@ +DIM a as Byte + +IF a >= 5 THEN + POKE 16384, 255 +END IF diff --git a/tests/functional/arch/zx81sd/geu16.asm b/tests/functional/arch/zx81sd/geu16.asm new file mode 100644 index 000000000..9a5741ffa --- /dev/null +++ b/tests/functional/arch/zx81sd/geu16.asm @@ -0,0 +1,137 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 01h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ex de, hl + ld hl, (_le) + or a + sbc hl, de + ccf + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_level) + ld hl, (_le) + or a + sbc hl, de + ccf + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld de, (_level) + pop hl + or a + sbc hl, de + ccf + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld hl, (_level) + ex de, hl + pop hl + or a + sbc hl, de + ccf + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ex de, hl + ld hl, 1 + or a + sbc hl, de + ccf + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/geu16.bas b/tests/functional/arch/zx81sd/geu16.bas new file mode 100644 index 000000000..b39f848b2 --- /dev/null +++ b/tests/functional/arch/zx81sd/geu16.bas @@ -0,0 +1,9 @@ +dim level as UInteger = 0 +dim le as UInteger = 1 +dim l as UInteger + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) +l = 1 >= (level + 0) diff --git a/tests/functional/arch/zx81sd/geu32.asm b/tests/functional/arch/zx81sd/geu32.asm new file mode 100644 index 000000000..71d7355ca --- /dev/null +++ b/tests/functional/arch/zx81sd/geu32.asm @@ -0,0 +1,210 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h +_le: + DEFB 01h + 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 + ccf + sbc a, a + neg + 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.__SUB32 + ccf + sbc a, a + neg + 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.__SUB32 + ccf + sbc a, a + neg + 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.__SUB32 + ccf + sbc a, a + neg + 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, 1 + push bc + call .core.__SUB32 + ccf + sbc a, a + neg + 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 + halt + ;; --- end of user code --- +#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 90 "arch/zx81sd/geu32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 91 "arch/zx81sd/geu32.bas" + END diff --git a/tests/functional/arch/zx81sd/geu32.bas b/tests/functional/arch/zx81sd/geu32.bas new file mode 100644 index 000000000..872f2700e --- /dev/null +++ b/tests/functional/arch/zx81sd/geu32.bas @@ -0,0 +1,9 @@ +dim level as ulong = 0 +dim le as ulong = 1 +dim l as ulong + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) +l = 1 >= (level + 0) diff --git a/tests/functional/arch/zx81sd/geu8.asm b/tests/functional/arch/zx81sd/geu8.asm new file mode 100644 index 000000000..5de371181 --- /dev/null +++ b/tests/functional/arch/zx81sd/geu8.asm @@ -0,0 +1,117 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 01h +_l: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_level) + ld h, a + ld a, (_le) + sub h + ccf + sbc a, a + neg + ld (_l), a + ld hl, (_level - 1) + ld a, (_le) + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_le) + push af + ld hl, (_level - 1) + pop af + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_le) + ld hl, (_level - 1) + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_level) + ld h, a + ld a, 1 + sub h + ccf + sbc a, a + neg + ld (_l), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/geu8.bas b/tests/functional/arch/zx81sd/geu8.bas new file mode 100644 index 000000000..864f91d8f --- /dev/null +++ b/tests/functional/arch/zx81sd/geu8.bas @@ -0,0 +1,9 @@ +dim level as UByte = 0 +dim le as UByte = 1 +dim l as UByte + +l = le >= (level + 0) +l = le >= level +l = (le + 0) >= level +l = (le + 0) >= (level + 0) +l = 1 >= (level + 0) diff --git a/tests/functional/arch/zx81sd/gtf.asm b/tests/functional/arch/zx81sd/gtf.asm new file mode 100644 index 000000000..3f91a63f4 --- /dev/null +++ b/tests/functional/arch/zx81sd/gtf.asm @@ -0,0 +1,2631 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h + DEFB 00h +_le: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__GTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__GTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__GTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__GTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/gtf.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 + 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/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/zx48k/runtime/cmp/gtf.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) + ; 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/zx48k/runtime/cmp/gtf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/gtf.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 +__GTF: ; A > B + call __FPSTACK_PUSH2 ; ENTERS B, A + ; ------------- ROM NOS-GRTR + ld b, 0Dh ; B < A + rst 28h + defb 0Dh ; B < A + defb 38h; ; END CALC + call __FPSTACK_POP + jp __FTOU8; Convert to 8 bits + pop namespace +#line 53 "arch/zx81sd/gtf.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 54 "arch/zx81sd/gtf.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 55 "arch/zx81sd/gtf.bas" + END diff --git a/tests/functional/arch/zx81sd/gtf.bas b/tests/functional/arch/zx81sd/gtf.bas new file mode 100644 index 000000000..fd1d22d36 --- /dev/null +++ b/tests/functional/arch/zx81sd/gtf.bas @@ -0,0 +1,8 @@ +dim level as Float = 0 +dim le as Float = 1 +dim l as Float + +l = le > (level + 0) +l = le > level +l = (le + 0) > level +l = (le + 0) > (level + 0) diff --git a/tests/functional/arch/zx81sd/gtf16.asm b/tests/functional/arch/zx81sd/gtf16.asm new file mode 100644 index 000000000..f93869be2 --- /dev/null +++ b/tests/functional/arch/zx81sd/gtf16.asm @@ -0,0 +1,225 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 01h + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + sub 1 + sbc a, a + neg + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lei32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 74 "arch/zx81sd/gtf16.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 75 "arch/zx81sd/gtf16.bas" + END diff --git a/tests/functional/arch/zx81sd/gtf16.bas b/tests/functional/arch/zx81sd/gtf16.bas new file mode 100644 index 000000000..1bbd0098f --- /dev/null +++ b/tests/functional/arch/zx81sd/gtf16.bas @@ -0,0 +1,8 @@ +dim level as fixed = 0 +dim le as fixed = 1 +dim l as fixed + +l = le > (level + 0) +l = le > level +l = (le + 0) > level +l = (le + 0) > (level + 0) diff --git a/tests/functional/arch/zx81sd/gti16.asm b/tests/functional/arch/zx81sd/gti16.asm new file mode 100644 index 000000000..d53d43c2c --- /dev/null +++ b/tests/functional/arch/zx81sd/gti16.asm @@ -0,0 +1,152 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 01h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_le) + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ld de, (_le) + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_le) + ld hl, (_level) + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_le) + ld hl, (_level) + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ld de, 1 + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 2 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti16.asm" + push namespace core +__LTI16: ; Test 8 bit values HL < DE + ; Returns result in A: 0 = False, !0 = True + PROC + LOCAL checkParity + or a + sbc hl, de + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 39 "arch/zx81sd/gti16.bas" + END diff --git a/tests/functional/arch/zx81sd/gti16.bas b/tests/functional/arch/zx81sd/gti16.bas new file mode 100644 index 000000000..617c48a6a --- /dev/null +++ b/tests/functional/arch/zx81sd/gti16.bas @@ -0,0 +1,9 @@ +dim level as Integer = 0 +dim le as Integer = 1 +dim l as Integer + +l = le > (level + 0) +l = le > level +l = (le + 0) > level +l = (le + 0) > (level + 0) +l = 1 > (level + 0) diff --git a/tests/functional/arch/zx81sd/gti32.asm b/tests/functional/arch/zx81sd/gti32.asm new file mode 100644 index 000000000..91f37074a --- /dev/null +++ b/tests/functional/arch/zx81sd/gti32.asm @@ -0,0 +1,241 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h +_le: + DEFB 01h + 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 + sub 1 + sbc a, a + neg + 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 + sub 1 + sbc a, a + neg + 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 + sub 1 + sbc a, a + neg + 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 + sub 1 + sbc a, a + neg + 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, 1 + push bc + call .core.__LEI32 + sub 1 + sbc a, a + neg + 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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lei32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 90 "arch/zx81sd/gti32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 91 "arch/zx81sd/gti32.bas" + END diff --git a/tests/functional/arch/zx81sd/gti32.bas b/tests/functional/arch/zx81sd/gti32.bas new file mode 100644 index 000000000..797e163b5 --- /dev/null +++ b/tests/functional/arch/zx81sd/gti32.bas @@ -0,0 +1,9 @@ +dim level as long = 0 +dim le as long = 1 +dim l as long + +l = le > (level + 0) +l = le > level +l = (le + 0) > level +l = (le + 0) > (level + 0) +l = 1 > (level + 0) diff --git a/tests/functional/arch/zx81sd/gti8.asm b/tests/functional/arch/zx81sd/gti8.asm new file mode 100644 index 000000000..1ccc4c549 --- /dev/null +++ b/tests/functional/arch/zx81sd/gti8.asm @@ -0,0 +1,107 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, 5 + ld hl, (_a - 1) + call .core.__LTI8 + or a + jp z, .LABEL.__LABEL1 + ld hl, _a + inc (hl) +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 2 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti8.asm" +#line 17 "arch/zx81sd/gti8.bas" + END diff --git a/tests/functional/arch/zx81sd/gti8.bas b/tests/functional/arch/zx81sd/gti8.bas new file mode 100644 index 000000000..9723d40fa --- /dev/null +++ b/tests/functional/arch/zx81sd/gti8.bas @@ -0,0 +1,5 @@ +DIM a as Byte + +IF a > 5 THEN + LET a = a + 1 +END IF diff --git a/tests/functional/arch/zx81sd/gtu16.asm b/tests/functional/arch/zx81sd/gtu16.asm new file mode 100644 index 000000000..684f16869 --- /dev/null +++ b/tests/functional/arch/zx81sd/gtu16.asm @@ -0,0 +1,125 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 01h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ld de, (_le) + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ld de, (_le) + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_le) + ld hl, (_level) + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_le) + ld hl, (_level) + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ld de, 1 + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/gtu16.bas b/tests/functional/arch/zx81sd/gtu16.bas new file mode 100644 index 000000000..7fde4043e --- /dev/null +++ b/tests/functional/arch/zx81sd/gtu16.bas @@ -0,0 +1,9 @@ +dim level as uinteger = 0 +dim le as uinteger = 1 +dim l as uinteger + +l = le > (level + 0) +l = le > level +l = (le + 0) > level +l = (le + 0) > (level + 0) +l = 1 > (level + 0) diff --git a/tests/functional/arch/zx81sd/gtu32.asm b/tests/functional/arch/zx81sd/gtu32.asm new file mode 100644 index 000000000..712a513e1 --- /dev/null +++ b/tests/functional/arch/zx81sd/gtu32.asm @@ -0,0 +1,201 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 00h + DEFB 00h +_le: + DEFB 01h + 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 + pop bc + or a + sbc hl, bc + ex de, hl + pop de + sbc hl, de + sbc a, a + neg + 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) + pop bc + or a + sbc hl, bc + ex de, hl + pop de + sbc hl, de + sbc a, a + neg + 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) + pop bc + or a + sbc hl, bc + ex de, hl + pop de + sbc hl, de + sbc a, a + neg + 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) + pop bc + or a + sbc hl, bc + ex de, hl + pop de + sbc hl, de + sbc a, a + neg + 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, 1 + or a + sbc hl, bc + ex de, hl + pop de + sbc hl, de + sbc a, a + neg + 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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 108 "arch/zx81sd/gtu32.bas" + END diff --git a/tests/functional/arch/zx81sd/gtu32.bas b/tests/functional/arch/zx81sd/gtu32.bas new file mode 100644 index 000000000..3360ad5ef --- /dev/null +++ b/tests/functional/arch/zx81sd/gtu32.bas @@ -0,0 +1,9 @@ +dim level as ulong = 0 +dim le as ulong = 1 +dim l as ulong + +l = le > (level + 0) +l = le > level +l = (le + 0) > level +l = (le + 0) > (level + 0) +l = 1 > (level + 0) diff --git a/tests/functional/arch/zx81sd/gtu8.asm b/tests/functional/arch/zx81sd/gtu8.asm new file mode 100644 index 000000000..ceddb7aad --- /dev/null +++ b/tests/functional/arch/zx81sd/gtu8.asm @@ -0,0 +1,81 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, 5 + ld hl, (_a - 1) + cp h + jp nc, .LABEL.__LABEL1 + ld hl, _a + inc (hl) +.LABEL.__LABEL1: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/gtu8.bas b/tests/functional/arch/zx81sd/gtu8.bas new file mode 100644 index 000000000..d0cc26c57 --- /dev/null +++ b/tests/functional/arch/zx81sd/gtu8.bas @@ -0,0 +1,5 @@ +DIM a as UByte + +IF a > 5 THEN + LET a = a + 1 +END IF diff --git a/tests/functional/arch/zx81sd/inkey.asm b/tests/functional/arch/zx81sd/inkey.asm new file mode 100644 index 000000000..2926d7a66 --- /dev/null +++ b/tests/functional/arch/zx81sd/inkey.asm @@ -0,0 +1,3193 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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.INKEY + ex de, hl + ld hl, _a + call .core.__STORE_STR2 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm" + ; INKEY Function (zx81sd) + ; Returns a string allocated in dynamic memory + ; containing the string. + ; An empty string otherwise. + ; + ; La version de zx48k llama a rutinas de la ROM del Spectrum + ; (KEY_SCAN/KEY_TEST/KEY_CODE) que no existen en tiempo de ejecucion en + ; zx81sd (no hay ROM mapeada). Se sustituye por un escaneo directo de la + ; matriz de teclado del ZX81 y una decodificacion propia a ASCII. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 12 "/zxbasic/src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/io/keyboard/keyscan.asm" + ; --------------------------------------------------------------------------- + ; keyscan.asm — Escaneo directo de la matriz de teclado del ZX81 + ; + ; No hay ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa por + ; completo nuestro binario compilado), asi que no se puede llamar a la + ; rutina KEYBOARD/DECODE de la ROM original. El hardware del teclado, en + ; cambio, es el mismo ZX81 de siempre (el SD81 Booster no lo toca), asi + ; que se reimplementa aqui el escaneo fisico (puerto $FEFE, 8 filas + ; seleccionadas rotando el registro alto del puerto) y una decodificacion + ; propia a ASCII. + ; + ; ESQUEMA DE TECLAS (2026-07-04, revisado tras probar en el emulador) + ; -------------------------------------------------------------------- + ; El teclado fisico del ZX81 no tiene mayuscula/minuscula por tecla (SHIFT + ; da simbolos, no la version en mayuscula de la letra) — a diferencia del + ; Spectrum, donde SHIFT+letra si da la mayuscula de esa letra via la ROM + ; (K-DECODE en modo L). Para poder imprimir el juego de caracteres +; completo sin depender de teclado externo: + ; + ; Sin modificador -> minuscula (a, s, d... como hasta ahora) + ; SHIFT + letra -> MAYUSCULA de esa letra + ; SHIFT + "2" -> conmuta CAPS LOCK (persistente; no imprime nada) + ; CAPS LOCK activo -> minuscula pasa a mayuscula (SHIFT sigue dando +; mayuscula igual, no hay interaccion: es un OR) + ; "." (con o sin SHIFT) -> "." o "," exactamente igual que en el ZX81 + ; real (SHIFT+"." = ",") + ; + ; SHIFT es comodo de mantener pulsado con una mano mientras se pulsa la + ; letra con la otra (aqui __ZX81SD_KEYSCAN SI detecta la simultaneidad + ; real, con dos lecturas de puerto dedicadas para SHIFT y una tercera + ; tecla). Pero un primer diseno que anadia un segundo modificador de + ; simbolo con "." (pulsar "."+tecla a la vez para sacar el simbolo +; impreso en el teclado del ZX81) resulto impracticable de teclear: al + ; usarse desde INPUT, la rutina de lectura ya compromete la tecla "." + ; en cuanto se detecta pulsada sola, sin dar tiempo a que la segunda + ; tecla llegue a la vez (no son dedos que se muevan en paralelo como + ; SHIFT+letra, sino una pulsacion despues de otra). Por eso ese segundo + ; modificador se ha movido a stdlib/input.bas, como una "tecla muerta" + ; a nivel de composicion de caracteres (se pulsa "." y LUEGO la tecla +; del simbolo, secuencialmente, sin necesidad de mantenerlas a la vez): + ; ver __ZX81SD_SYMBOL_FOR mas abajo, que da el mapeo tecla->simbolo que + ; usa esa logica. + ; +; IMPORTANTE: la ROM del ZX81 decodifica las teclas a SU PROPIO charset + ; (con codigos de token para palabras clave de BASIC), no a ASCII. El + ; runtime de zx81sd usa el charset Spectrum/ASCII para imprimir (ver + ; charset.asm, specfont.bin), asi que las tablas de decodificacion de + ; abajo traducen directamente cada tecla a su codigo ASCII, ignorando las + ; teclas que en el ZX81 producen tokens de BASIC sin equivalente ASCII + ; simple (STOP, AND, OR, THEN, TO, STEP, <=, >=, <>, **, EDIT, GRAPHICS, + ; FUNCTION, cursores, LPRINT, LLIST, SLOW, FAST). La tecla RUBOUT + ; (SHIFT+0) se traduce a ASCII 12, y ENTER (NEWLINE) a ASCII 13, para + ; mantener compatibilidad con la convencion ya usada por stdlib/input.bas. + ; --------------------------------------------------------------------------- + push namespace core + ; --- Tablas de decodificacion, en ambito de fichero (no LOCAL a ningun +; PROC): las usa __ZX81SD_KEYSCAN y tambien __ZX81SD_SYMBOL_FOR. --- + ; + ; Orden de filas/columnas identico al de las tablas K-UNSHIFT/K-SHIFT de +; la ROM original del ZX81 (fila 0: SHIFT,Z,X,C,V ... fila 7: ENTER,L,K, + ; J,H / SPACE,.,M,N,B), verificado contra el disassembly. +__ZX81SD_UNSHIFT_TABLE: + DEFB 'z', 'x', 'c', 'v' + DEFB 'a', 's', 'd', 'f', 'g' + DEFB 'q', 'w', 'e', 'r', 't' + DEFB '1', '2', '3', '4', '5' + DEFB '0', '9', '8', '7', '6' + DEFB 'p', 'o', 'i', 'u', 'y' + DEFB 13, 'l', 'k', 'j', 'h' ; NEWLINE -> ENTER (ASCII 13) + DEFB ' ', '.', 'm', 'n', 'b' + ; __ZX81SD_SYMBOL_TABLE — simbolos impresos en el teclado del ZX81 bajo + ; SHIFT. Alineada posicion a posicion con __ZX81SD_UNSHIFT_TABLE (mismo + ; indice = misma tecla fisica); la usa __ZX81SD_SYMBOL_FOR para el modo + ; de composicion "." + tecla de stdlib/input.bas. +__ZX81SD_SYMBOL_TABLE: + DEFB ':', ';', '?', '/' + DEFB 0, 0, 0, 0, 0 ; STOP, LPRINT, SLOW, FAST, LLIST + DEFB '"', 0, 0, 0, 0 ; "" (par de comillas), OR, STEP, <=, <> + DEFB 0, 0, 0, 0, 0 ; EDIT, [CAPS LOCK], THEN, TO, cursor-izq + DEFB 12, 0, 0, 0, 0 ; RUBOUT (DEL=12), GRAPHICS, cursor der/arr/abj + DEFB '"', ')', '(', '$', 0 ; ", ), (, $, >= + DEFB 0, '=', '+', '-', 0 ; FUNCTION, =, +, -, ** + DEFB 0, ',', '>', '<', '*' ; £, ',', >, <, * + ; __ZX81SD_CAPS_TABLE — igual que __ZX81SD_SYMBOL_TABLE, pero con la + ; MAYUSCULA en cada posicion que en UNSHIFT_TABLE es una letra (para + ; SHIFT+letra, y para el modo CAPS LOCK persistente). Las posiciones que + ; no son letra se dejan igual que en SYMBOL_TABLE (digitos, ENTER, + ; SPACE, RUBOUT...). +__ZX81SD_CAPS_TABLE: + DEFB 'Z', 'X', 'C', 'V' + DEFB 'A', 'S', 'D', 'F', 'G' + DEFB 'Q', 'W', 'E', 'R', 'T' + DEFB 0, 0, 0, 0, 0 ; digitos 1-5 (el "2" se intercepta antes) + DEFB 12, 0, 0, 0, 0 ; digitos 0,9,8,7,6 (RUBOUT en el "0") + DEFB 'P', 'O', 'I', 'U', 'Y' + DEFB 0, 'L', 'K', 'J', 'H' ; ENTER sin cambio, L/K/J/H en mayuscula + DEFB 0, ',', 'M', 'N', 'B' ; SPACE sin cambio, "." no se alcanza aqui + ; --- Estado persistente del teclado (sobrevive entre llamadas) --- +__ZX81SD_KBD_CAPSLOCK: DEFB 0 ; 1 = CAPS LOCK activo +__ZX81SD_KBD_CAPS_EDGE: DEFB 0 ; deteccion de flanco para el combo SHIFT+"2" + ; --- Estado transitorio (se recalcula en cada llamada a __ZX81SD_KEYSCAN) --- +__ZX81SD_KBD_SHIFT: DEFB 0 +__ZX81SD_KBD_OTHER_VALID: DEFB 0 +__ZX81SD_KBD_OTHER_IDX: DEFB 0 + ; --------------------------------------------------------------------------- + ; __ZX81SD_KEYSCAN + ; + ; Escanea el teclado y decodifica la tecla pulsada segun el esquema de + ; arriba (SHIFT como modificador de mayuscula, CAPS LOCK persistente). + ; +; Devuelve: + ; A = codigo ASCII de la tecla pulsada, o 0 si no hay ninguna pulsada, + ; si la combinacion no tiene equivalente ASCII, si solo esta + ; pulsado SHIFT solo, o si se acaba de conmutar el CAPS LOCK + ; (SHIFT+"2"). + ; Flag Z activo si A = 0 + ; +; Registros modificados: AF, BC, DE, HL + ; --------------------------------------------------------------------------- +__ZX81SD_KEYSCAN: + PROC + LOCAL FIND_OTHER + LOCAL ROW_LOOP + LOCAL NEXT_ROW + LOCAL FIND_COL + LOCAL GOT_COL0 + LOCAL GOT_COL1 + LOCAL GOT_COL2 + LOCAL GOT_COL3 + LOCAL GOT_COL4 + LOCAL GOT_KEY + LOCAL OTHER_FOUND + LOCAL OTHER_NONE + LOCAL NOT_ROW0 + LOCAL DECIDE + LOCAL HAVE_SHIFT + LOCAL SHIFT_ALONE + LOCAL CHECK_CAPS_KEY + LOCAL DO_CAPS_TOGGLE + LOCAL CAPS_EDGE_DONE + LOCAL NO_SHIFT + LOCAL NO_KEY + LOCAL USE_UNSHIFT + LOCAL USE_CAPS_FOR_PLAIN + LOCAL LOOKUP_OTHER_IN_HL + LOCAL RESET_CAPS_EDGE + LOCAL SHIFT_BIT_SET + ; --- 1. Leer SHIFT (fila 0, columna 0) con una lectura dedicada --- + ld bc, $FEFE + in a, (c) + and $01 + ld a, 1 + jr z, SHIFT_BIT_SET + xor a +SHIFT_BIT_SET: + ld (__ZX81SD_KBD_SHIFT), a + ; --- 2. Buscar otra tecla pulsada (excluyendo SHIFT) --- + xor a + ld (__ZX81SD_KBD_OTHER_VALID), a + call FIND_OTHER ; carry activo = encontrada (indice en A) + jr nc, DECIDE + ld (__ZX81SD_KBD_OTHER_IDX), a + ld a, 1 + ld (__ZX81SD_KBD_OTHER_VALID), a +DECIDE: + ld a, (__ZX81SD_KBD_SHIFT) + or a + jr nz, HAVE_SHIFT + jr NO_SHIFT +HAVE_SHIFT: + ld a, (__ZX81SD_KBD_OTHER_VALID) + or a + jr nz, CHECK_CAPS_KEY +SHIFT_ALONE: + ; Solo SHIFT: sin caracter, y se reinicia la deteccion de flanco del + ; combo SHIFT+"2" (no puede haber toggle sin la tecla "2" presente). + call RESET_CAPS_EDGE + xor a + ret +CHECK_CAPS_KEY: + ld a, (__ZX81SD_KBD_OTHER_IDX) + cp 15 ; indice de la tecla "2" (fila 3, columna 1) + jr z, DO_CAPS_TOGGLE + ; SHIFT + letra (o cualquier otra tecla, salvo "2"): __ZX81SD_CAPS_TABLE + ; (mayuscula en las posiciones de letra; en el resto, igual que + ; __ZX81SD_SYMBOL_TABLE — p.ej. SHIFT+"." sigue dando ","). + call RESET_CAPS_EDGE + ld hl, __ZX81SD_CAPS_TABLE + jr LOOKUP_OTHER_IN_HL +DO_CAPS_TOGGLE: + ; Conmuta CAPS LOCK solo en el flanco de subida del combo (para no + ; conmutarlo en cada llamada mientras se mantiene pulsado). + ld a, (__ZX81SD_KBD_CAPS_EDGE) + or a + jr nz, CAPS_EDGE_DONE + ld a, 1 + ld (__ZX81SD_KBD_CAPS_EDGE), a + ld a, (__ZX81SD_KBD_CAPSLOCK) + xor 1 + ld (__ZX81SD_KBD_CAPSLOCK), a +CAPS_EDGE_DONE: + xor a ; SHIFT+"2" es mudo: nunca devuelve caracter + ret +NO_SHIFT: + call RESET_CAPS_EDGE + ld a, (__ZX81SD_KBD_OTHER_VALID) + or a + jr z, NO_KEY + ; Sin SHIFT: __ZX81SD_UNSHIFT_TABLE (minuscula), salvo que CAPS LOCK + ; este activo, en cuyo caso se usa __ZX81SD_CAPS_TABLE (mayuscula en + ; las letras). "." cae aqui igual que cualquier otra tecla: siempre + ; devuelve "." (o "," si SHIFT, ya cubierto arriba). + ld a, (__ZX81SD_KBD_CAPSLOCK) + or a + jr z, USE_UNSHIFT + jr USE_CAPS_FOR_PLAIN +USE_UNSHIFT: + ld hl, __ZX81SD_UNSHIFT_TABLE + jr LOOKUP_OTHER_IN_HL +USE_CAPS_FOR_PLAIN: + ld hl, __ZX81SD_CAPS_TABLE +LOOKUP_OTHER_IN_HL: + ld a, (__ZX81SD_KBD_OTHER_IDX) + ld e, a + ld d, 0 + add hl, de + ld a, (hl) + or a + ret +NO_KEY: + xor a + ret +RESET_CAPS_EDGE: + xor a + ld (__ZX81SD_KBD_CAPS_EDGE), a + ret + ; --------------------------------------------------------------------------- + ; FIND_OTHER — busca una tecla pulsada distinta de SHIFT (fila 0, col 0). + ; Devuelve acarreo INACTIVO si no encuentra ninguna; si encuentra una, + ; acarreo ACTIVO y A = indice (0-38, formula fila*5+columna-1). Se usa el + ; acarreo (no Z) porque el indice 0 (tecla Z) es un resultado valido con + ; A=0, que haria Z activo por error si se usara ese flag para + ; "encontrada/no encontrada". +; Registros modificados: AF, BC, DE, HL. + ; --------------------------------------------------------------------------- +FIND_OTHER: + ld d, 0 ; D = indice de fila actual (0-7) + ld b, $FE ; B = mitad alta del puerto ($FEFE...$7FFE) +ROW_LOOP: + ld c, $FE + in a, (c) ; lee la fila; bits 0-4 = columnas (0 = pulsada) + and $1F + ld l, a ; L = mapa de bits de columnas pulsadas + ld a, d + or a + jr nz, NOT_ROW0 + set 0, l ; fila 0: descartar SHIFT (columna 0) +NOT_ROW0: + ld a, l + cp $1F + jr z, NEXT_ROW ; nada mas pulsado en esta fila +FIND_COL: + ; Bucle desenrollado a proposito: usar CP para comparar el contador + ; de columna sobreescribia A (el mapa de bits que se iba rotando) + ; antes de que la siguiente vuelta pudiera comprobarlo, con lo que + ; solo la columna 0 se detectaba bien. Sin bucle no hay ese riesgo. + ld a, l + rrca + jr nc, GOT_COL0 + rrca + jr nc, GOT_COL1 + rrca + jr nc, GOT_COL2 + rrca + jr nc, GOT_COL3 + rrca + jr nc, GOT_COL4 + jr NEXT_ROW ; no deberia ocurrir (ya se comprobo cp $1F antes) +GOT_COL0: + ld c, 0 + jr GOT_KEY +GOT_COL1: + ld c, 1 + jr GOT_KEY +GOT_COL2: + ld c, 2 + jr GOT_KEY +GOT_COL3: + ld c, 3 + jr GOT_KEY +GOT_COL4: + ld c, 4 +GOT_KEY: + ; indice de tabla = fila*5 + columna - 1 + ; (la fila 0 solo aporta 4 teclas: columnas 1-4, de ahi el -1) + ld a, d + add a, a + add a, a + add a, d ; A = fila*5 + add a, c ; A = fila*5 + columna + dec a ; A = indice (0-38) + jr OTHER_FOUND +NEXT_ROW: + rlc b + inc d + ld a, d + cp 8 + jr nz, ROW_LOOP +OTHER_NONE: + xor a + ret ; carry inactivo (xor siempre lo limpia) +OTHER_FOUND: + scf ; carry activo; A ya tiene el indice (GOT_KEY) + ret + ENDP + ; --------------------------------------------------------------------------- + ; __ZX81SD_SYMBOL_FOR — dado un caracter ASCII (el que devolveria una + ; pulsacion normal sin modificadores, es decir uno de los que aparecen en + ; __ZX81SD_UNSHIFT_TABLE), devuelve el simbolo del ZX81 que le + ; corresponde bajo el antiguo modificador SHIFT del ZX81 (o 0 si esa + ; tecla no tiene simbolo). La usa stdlib/input.bas para componer simbolos +; como una "tecla muerta": el usuario pulsa "." y LUEGO esta tecla, sin + ; necesitar mantenerlas pulsadas a la vez (ver cabecera del fichero). + ; +; Entrada: A = caracter ASCII de la segunda tecla +; Salida: A = simbolo del ZX81, o 0 si esa tecla no tiene simbolo +; Registros modificados: AF, BC, DE, HL + ; --------------------------------------------------------------------------- +__ZX81SD_SYMBOL_FOR: + PROC + LOCAL SEARCH_LOOP + LOCAL FOUND + LOCAL NOT_FOUND + ld c, a ; C = caracter buscado + ld hl, __ZX81SD_UNSHIFT_TABLE + ld de, __ZX81SD_SYMBOL_TABLE + ld b, 39 ; numero de entradas de ambas tablas +SEARCH_LOOP: + ld a, (hl) + cp c + jr z, FOUND + inc hl + inc de + djnz SEARCH_LOOP + jr NOT_FOUND +FOUND: + ld a, (de) + or a + ret +NOT_FOUND: + xor a + ret + ENDP + pop namespace +#line 13 "/zxbasic/src/lib/arch/zx81sd/runtime/io/keyboard/inkey.asm" + push namespace core +INKEY: + PROC + LOCAL __EMPTY_INKEY + ld bc, 3 ; 1 char length string + call __MEM_ALLOC + ld a, h + or l + ret z ; Return if NULL (No memory) + push hl ; Saves memory pointer + call __ZX81SD_KEYSCAN + pop hl + or a + jr z, __EMPTY_INKEY + ld (hl), 1 + inc hl + ld (hl), 0 + inc hl + ld (hl), a + dec hl + dec hl ; HL Points to string result + ret +__EMPTY_INKEY: + xor a + ld (hl), a + inc hl + ld (hl), a + dec hl + ret + ENDP + pop namespace +#line 13 "arch/zx81sd/inkey.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/mem/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 9 "/zxbasic/src/lib/arch/zx48k/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 14 "arch/zx81sd/inkey.bas" + END diff --git a/tests/functional/arch/zx81sd/inkey.bas b/tests/functional/arch/zx81sd/inkey.bas new file mode 100644 index 000000000..8480dacfc --- /dev/null +++ b/tests/functional/arch/zx81sd/inkey.bas @@ -0,0 +1 @@ +a$ = inkey$ diff --git a/tests/functional/arch/zx81sd/lbound12.asm b/tests/functional/arch/zx81sd/lbound12.asm new file mode 100644 index 000000000..9d2fe7581 --- /dev/null +++ b/tests/functional/arch/zx81sd/lbound12.asm @@ -0,0 +1,3149 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test1 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_test3: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + ld (_b), hl + jp .LABEL.__LABEL0 +.LABEL.__LABEL3: + ld hl, (_b) + push hl + ld l, (ix+4) + ld h, (ix+5) + call .core.__LBOUND + ld (_c), hl +.LABEL.__LABEL4: + ld hl, (_b) + inc hl + ld (_b), hl +.LABEL.__LABEL0: + ld hl, 3 + ld de, (_b) + or a + sbc hl, de + jp nc, .LABEL.__LABEL3 +.LABEL.__LABEL2: +_test3__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_test1: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + push hl + ld hl, 0 + push hl + ld hl, _test1.a1.__LBOUND__ + push hl + ld hl, -6 + ld de, .LABEL.__LABEL5 + ld bc, 18 + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS + push ix + pop hl + ld de, -6 + add hl, de + push hl + call _test2 +_test1__leave: + ex af, af' + exx + ld hl, 9 + push hl + ld l, (ix-4) + ld h, (ix-3) + call .core.__ARRAYSTR_FREE_MEM + ex af, af' + exx + ld sp, ix + pop ix + ret +_test1.a1.__LBOUND__: + DEFW 0003h + DEFW 0007h +_test2: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + call _test3 +_test2__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 97 "arch/zx81sd/lbound12.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" + ; --------------------------------------------------------- + ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) +; http://www.boriel.com + ; +; ZX BASIC Compiler http://www.zxbasic.net + ; This code is released under the BSD License + ; --------------------------------------------------------- + ; Implements both LBOUND(array, N) and UBOUND(array, N) function +; Parameters: + ; HL = PTR to array + ; [stack - 2] -> N (dimension) + push namespace core + PROC + LOCAL __BOUND + LOCAL __DIM_NOT_EXIST + LOCAL __CONT +__LBOUND: + ld a, 4 + jr __BOUND +__UBOUND: + ld a, 6 +__BOUND: + ex de, hl ; DE <-- Array ptr + pop hl ; HL <-- Ret address + ex (sp), hl ; CALLEE: HL <-- N, (SP) <-- Ret address + ex de, hl ; DE <-- N, HL <-- ARRAY_PTR + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = start of dimension table (first position contains number of dimensions - 1) + ld c, (hl) + inc hl + ld b, (hl) + inc bc ; Number of total dimensions of the array + pop hl ; Recovers ARRAY PTR + ex af, af' ; Saves A for later + ld a, d + or e + jr nz, __CONT ; N = 0 => Return number of dimensions + ;; Return the number of dimensions of the array + ld h, b + ld l, c + ret +__CONT: + dec de + ex af, af' ; Recovers A (contains PTR offset) + ex de, hl ; HL = N (dimension asked) - 1, DE = Array PTR + or a + sbc hl, bc ; if no Carry => the user asked for a dimension that does not exist. Return 0 + jr nc, __DIM_NOT_EXIST + add hl, bc ; restores HL = (N - 1) + add hl, hl ; hl *= 2 + ex de, hl ; hl = ARRAY_PTR + 3, DE jsz = (N - 1) * 2 + ld b, 0 + ld c, a + add hl, bc ; HL = &BOUND_PTR + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; LD HL, (HL) => Origin of L/U Bound table + ; for LBound only, HL = 0x0000 (NULL) if the array is all 0-based + or h + ret z ; Should never happen for UBound + add hl, de ; hl += OFFSET __LBOUND._xxxx + ld e, (hl) ; de = (hl) + inc hl + ld d, (hl) + ex de, hl ; hl = de => returns result in HL + ret +__DIM_NOT_EXIST: + ; The dimension requested by the user does not exists. Return 0 + ld hl, 0 + ret + ENDP + pop namespace +#line 98 "arch/zx81sd/lbound12.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" + ; This routine is in charge of freeing an array of strings from memory + ; HL = Pointer to start of array in memory + ; Top of the stack = Number of elements of the array +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 6 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" + push namespace core +__ARRAYSTR_FREE: + PROC + LOCAL __ARRAY_LOOP + ex de, hl + pop hl ; (ret address) + ex (sp), hl ; Callee -> HL = Number of elements + ex de, hl +__ARRAYSTR_FREE_FAST: ; Fastcall entry: DE = Number of elements + ld a, h + or l + ret z ; ret if NULL + ld b, d + ld c, e +__ARRAY_LOOP: + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = (HL) = String Pointer + push hl + push bc + ex de, hl + call __MEM_FREE ; Frees it from memory + pop bc + pop hl + dec bc + ld a, b + or c + jp nz, __ARRAY_LOOP + ret ; Frees it and return + ENDP +__ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself + ex de, hl + pop hl ; (ret address) + ex (sp), hl ; Callee -> HL = Number of elements + ex de, hl + push hl ; Saves array pointer for later + call __ARRAYSTR_FREE_FAST + pop hl ; recovers array block pointer + jp __MEM_FREE ; Frees it and returns from __MEM_FREE + pop namespace +#line 99 "arch/zx81sd/lbound12.bas" +.LABEL.__LABEL5: + DEFB 01h + DEFB 00h + DEFB 03h + DEFB 00h + DEFB 02h + END diff --git a/tests/functional/arch/zx81sd/lbound12.bas b/tests/functional/arch/zx81sd/lbound12.bas new file mode 100644 index 000000000..8f052f83b --- /dev/null +++ b/tests/functional/arch/zx81sd/lbound12.bas @@ -0,0 +1,21 @@ + +DIM b, c as UInteger + +DECLARE SUB test2(a2() as String) + +SUB test3(a3() as String) + FOR b = 0 TO 3 + c = LBound(a3, b) + NEXT +END SUB + +SUB test1() + DIM a1(3 TO 5, 7 TO 9) As String + test2(a1) +END SUB + +SUB test2(a2() as String) + test3(a2) +END SUB + +test1() diff --git a/tests/functional/arch/zx81sd/lef.asm b/tests/functional/arch/zx81sd/lef.asm new file mode 100644 index 000000000..2749e47ef --- /dev/null +++ b/tests/functional/arch/zx81sd/lef.asm @@ -0,0 +1,2644 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__LEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__LEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__LEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__LEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, 00000h + push hl + ld hl, 00000h + push hl + ld h, 000h + push hl + call .core.__LEF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lef.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 + 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/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/zx48k/runtime/cmp/lef.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) + ; 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/zx48k/runtime/cmp/lef.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lef.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 +__LEF: ; A <= B + call __FPSTACK_PUSH2 ; B, A + ; ------------- ROM NO-L-EQL + ld b, 0Ah ; B => A + rst 28h + defb 0Ah ; B => A + defb 38h; ; END CALC + call __FPSTACK_POP + jp __FTOU8 ; Convert to 8 bits + pop namespace +#line 66 "arch/zx81sd/lef.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 67 "arch/zx81sd/lef.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 68 "arch/zx81sd/lef.bas" + END diff --git a/tests/functional/arch/zx81sd/lef.bas b/tests/functional/arch/zx81sd/lef.bas new file mode 100644 index 000000000..f5db6cb26 --- /dev/null +++ b/tests/functional/arch/zx81sd/lef.bas @@ -0,0 +1,9 @@ +dim level as Float = 1 +dim le as Float = 0 +dim l as Float + +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/zx81sd/lei16.asm b/tests/functional/arch/zx81sd/lei16.asm new file mode 100644 index 000000000..32dad641c --- /dev/null +++ b/tests/functional/arch/zx81sd/lei16.asm @@ -0,0 +1,137 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ex de, hl + ld hl, (_le) + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_level) + ld hl, (_le) + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld de, (_level) + pop hl + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld hl, (_level) + ex de, hl + pop hl + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ex de, hl + ld hl, 0 + call .core.__LEI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lei16.asm" + push namespace core +__LEI16: + PROC + LOCAL checkParity + or a + sbc hl, de + ld a, 1 + ret z + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 46 "arch/zx81sd/lei16.bas" + END diff --git a/tests/functional/arch/zx81sd/lei16.bas b/tests/functional/arch/zx81sd/lei16.bas new file mode 100644 index 000000000..2a3929ec0 --- /dev/null +++ b/tests/functional/arch/zx81sd/lei16.bas @@ -0,0 +1,9 @@ +dim level as Integer = 1 +dim le as Integer = 0 +dim l as Integer + +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/zx81sd/lei32.asm b/tests/functional/arch/zx81sd/lei32.asm new file mode 100644 index 000000000..d097865a7 --- /dev/null +++ b/tests/functional/arch/zx81sd/lei32.asm @@ -0,0 +1,226 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lei32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 75 "arch/zx81sd/lei32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 76 "arch/zx81sd/lei32.bas" + END diff --git a/tests/functional/arch/zx81sd/lei32.bas b/tests/functional/arch/zx81sd/lei32.bas new file mode 100644 index 000000000..6c1cbeacf --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/lei8.asm b/tests/functional/arch/zx81sd/lei8.asm new file mode 100644 index 000000000..54ff89f27 --- /dev/null +++ b/tests/functional/arch/zx81sd/lei8.asm @@ -0,0 +1,125 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 33 "arch/zx81sd/lei8.bas" + END diff --git a/tests/functional/arch/zx81sd/lei8.bas b/tests/functional/arch/zx81sd/lei8.bas new file mode 100644 index 000000000..38193ce3a --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/leu8.asm b/tests/functional/arch/zx81sd/leu8.asm new file mode 100644 index 000000000..d695b09d4 --- /dev/null +++ b/tests/functional/arch/zx81sd/leu8.asm @@ -0,0 +1,116 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 hl, (_le - 1) + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_level) + ld hl, (_le - 1) + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_le) + push af + ld a, (_level) + pop hl + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_le) + push af + ld a, (_level) + pop hl + sub h + ccf + sbc a, a + neg + ld (_l), a + ld a, (_level) + or a + ccf + sbc a, a + neg + ld (_l), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/leu8.bas b/tests/functional/arch/zx81sd/leu8.bas new file mode 100644 index 000000000..951ddff86 --- /dev/null +++ b/tests/functional/arch/zx81sd/leu8.bas @@ -0,0 +1,9 @@ +dim level as UByte = 1 +dim le as UByte = 0 +dim l as UByte + +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/zx81sd/ltf.asm b/tests/functional/arch/zx81sd/ltf.asm new file mode 100644 index 000000000..15846306c --- /dev/null +++ b/tests/functional/arch/zx81sd/ltf.asm @@ -0,0 +1,2631 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_le: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__LTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + ld hl, _le + 4 + call .core.__FP_PUSH_REV + call .core.__LTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__LTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld a, (_le) + ld de, (_le + 1) + ld bc, (_le + 3) + push bc + push de + push af + ld a, (_level) + ld de, (_level + 1) + ld bc, (_level + 3) + call .core.__LTF + call .core.__U8TOFREG + ld hl, _l + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/ltf.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 + 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/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/zx48k/runtime/cmp/ltf.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) + ; 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/zx48k/runtime/cmp/ltf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/ltf.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 HL BC. + ; + ; Uses CALLEE convention + ; ------------------------------------------------------------- + push namespace core +__LTF: ; A < B + call __FPSTACK_PUSH2 ; Enters B, A + ; ------------- ROM NOS-LESS + ld b, 0Ch ; A > B (Operands stack-reversed) + rst 28h + defb 0Ch; ; A > B + defb 38h; ; END CALC + call __FPSTACK_POP + jp __FTOU8 ; Convert to 8 bits + pop namespace +#line 53 "arch/zx81sd/ltf.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 54 "arch/zx81sd/ltf.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 55 "arch/zx81sd/ltf.bas" + END diff --git a/tests/functional/arch/zx81sd/ltf.bas b/tests/functional/arch/zx81sd/ltf.bas new file mode 100644 index 000000000..d88a50279 --- /dev/null +++ b/tests/functional/arch/zx81sd/ltf.bas @@ -0,0 +1,8 @@ +dim level as Float = 1 +dim le as Float = 0 +dim l as Float + +l = le < (level + 0) +l = le < level +l = (le + 0) < level +l = (le + 0) < (level + 0) diff --git a/tests/functional/arch/zx81sd/ltf16.asm b/tests/functional/arch/zx81sd/ltf16.asm new file mode 100644 index 000000000..a07480a50 --- /dev/null +++ b/tests/functional/arch/zx81sd/ltf16.asm @@ -0,0 +1,205 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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.__LTI32 + ld l, a + ld h, 0 + ex de, hl + ld hl, 0 + 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 + ex de, hl + ld hl, 0 + 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 + ex de, hl + ld hl, 0 + 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 + ex de, hl + ld hl, 0 + ld (_l), hl + ld (_l + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 62 "arch/zx81sd/ltf16.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/ltf16.bas" + END diff --git a/tests/functional/arch/zx81sd/ltf16.bas b/tests/functional/arch/zx81sd/ltf16.bas new file mode 100644 index 000000000..8d9168ee7 --- /dev/null +++ b/tests/functional/arch/zx81sd/ltf16.bas @@ -0,0 +1,8 @@ +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 +l = (le + 0) < (level + 0) diff --git a/tests/functional/arch/zx81sd/lti16.asm b/tests/functional/arch/zx81sd/lti16.asm new file mode 100644 index 000000000..1a49f35a0 --- /dev/null +++ b/tests/functional/arch/zx81sd/lti16.asm @@ -0,0 +1,159 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ex de, hl + ld hl, (_le) + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_level) + ld hl, (_le) + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld de, (_level) + pop hl + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld hl, (_level) + ex de, hl + pop hl + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ex de, hl + ld hl, 0 + call .core.__LTI16 + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 2 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti16.asm" + push namespace core +__LTI16: ; Test 8 bit values HL < DE + ; Returns result in A: 0 = False, !0 = True + PROC + LOCAL checkParity + or a + sbc hl, de + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 46 "arch/zx81sd/lti16.bas" + END diff --git a/tests/functional/arch/zx81sd/lti16.bas b/tests/functional/arch/zx81sd/lti16.bas new file mode 100644 index 000000000..9b0eb59c5 --- /dev/null +++ b/tests/functional/arch/zx81sd/lti16.bas @@ -0,0 +1,9 @@ +dim level as Integer = 1 +dim le as Integer = 0 +dim l as Integer + +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/zx81sd/lti32.asm b/tests/functional/arch/zx81sd/lti32.asm new file mode 100644 index 000000000..b088f73a8 --- /dev/null +++ b/tests/functional/arch/zx81sd/lti32.asm @@ -0,0 +1,218 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti32.asm" +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 75 "arch/zx81sd/lti32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 76 "arch/zx81sd/lti32.bas" + END diff --git a/tests/functional/arch/zx81sd/lti32.bas b/tests/functional/arch/zx81sd/lti32.bas new file mode 100644 index 000000000..311846907 --- /dev/null +++ b/tests/functional/arch/zx81sd/lti32.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/zx81sd/lti8.asm b/tests/functional/arch/zx81sd/lti8.asm new file mode 100644 index 000000000..a86083baf --- /dev/null +++ b/tests/functional/arch/zx81sd/lti8.asm @@ -0,0 +1,127 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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.__LTI8 + ld (_l), a + ld hl, (_level - 1) + ld a, (_le) + call .core.__LTI8 + ld (_l), a + ld a, (_le) + push af + ld hl, (_level - 1) + pop af + call .core.__LTI8 + ld (_l), a + ld a, (_le) + ld hl, (_level - 1) + call .core.__LTI8 + ld (_l), a + ld a, (_level) + ld h, a + xor a + call .core.__LTI8 + ld (_l), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 2 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti8.asm" +#line 33 "arch/zx81sd/lti8.bas" + END diff --git a/tests/functional/arch/zx81sd/lti8.bas b/tests/functional/arch/zx81sd/lti8.bas new file mode 100644 index 000000000..7d5ec8297 --- /dev/null +++ b/tests/functional/arch/zx81sd/lti8.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/zx81sd/ltu16.asm b/tests/functional/arch/zx81sd/ltu16.asm new file mode 100644 index 000000000..86a278e81 --- /dev/null +++ b/tests/functional/arch/zx81sd/ltu16.asm @@ -0,0 +1,132 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_le: + DEFB 00h + DEFB 00h +_l: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_level) + ex de, hl + ld hl, (_le) + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld de, (_level) + ld hl, (_le) + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld de, (_level) + pop hl + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_le) + push hl + ld hl, (_level) + ex de, hl + pop hl + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, (_level) + ex de, hl + ld hl, 0 + or a + sbc hl, de + sbc a, a + neg + ld l, a + ld h, 0 + ld (_l), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/ltu16.bas b/tests/functional/arch/zx81sd/ltu16.bas new file mode 100644 index 000000000..fffb43bdc --- /dev/null +++ b/tests/functional/arch/zx81sd/ltu16.bas @@ -0,0 +1,9 @@ +dim level as UInteger = 1 +dim le as UInteger = 0 +dim l as UInteger + +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/zx81sd/ltu32.asm b/tests/functional/arch/zx81sd/ltu32.asm new file mode 100644 index 000000000..7947a2c86 --- /dev/null +++ b/tests/functional/arch/zx81sd/ltu32.asm @@ -0,0 +1,205 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + sbc a, a + neg + 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.__SUB32 + sbc a, a + neg + 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.__SUB32 + sbc a, a + neg + 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.__SUB32 + sbc a, a + neg + 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.__SUB32 + sbc a, a + neg + 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 + halt + ;; --- end of user code --- +#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 85 "arch/zx81sd/ltu32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 86 "arch/zx81sd/ltu32.bas" + END diff --git a/tests/functional/arch/zx81sd/ltu32.bas b/tests/functional/arch/zx81sd/ltu32.bas new file mode 100644 index 000000000..3c16c609f --- /dev/null +++ b/tests/functional/arch/zx81sd/ltu32.bas @@ -0,0 +1,9 @@ +dim level as ulong = 1 +dim le as ulong = 0 +dim l as ulong + +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/zx81sd/ltu8.asm b/tests/functional/arch/zx81sd/ltu8.asm new file mode 100644 index 000000000..ca747fc92 --- /dev/null +++ b/tests/functional/arch/zx81sd/ltu8.asm @@ -0,0 +1,112 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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) + cp h + sbc a, a + neg + ld (_l), a + ld hl, (_level - 1) + ld a, (_le) + cp h + sbc a, a + neg + ld (_l), a + ld a, (_le) + push af + ld hl, (_level - 1) + pop af + cp h + sbc a, a + neg + ld (_l), a + ld a, (_le) + ld hl, (_level - 1) + cp h + sbc a, a + neg + ld (_l), a + ld a, (_level) + ld h, a + xor a + cp h + sbc a, a + neg + ld (_l), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/ltu8.bas b/tests/functional/arch/zx81sd/ltu8.bas new file mode 100644 index 000000000..3a5a0b6ec --- /dev/null +++ b/tests/functional/arch/zx81sd/ltu8.bas @@ -0,0 +1,9 @@ +dim level as UByte = 1 +dim le as UByte = 0 +dim l as UByte + +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/zx81sd/math_acs.asm b/tests/functional/arch/zx81sd/math_acs.asm new file mode 100644 index 000000000..42acd5c23 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_acs.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.ACOS + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/acos.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/acos.asm" + push namespace core +ACOS: ; Computes ACOS using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 23h ; ACOS + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_acs.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 16 "arch/zx81sd/math_acs.bas" + END diff --git a/tests/functional/arch/zx81sd/math_acs.bas b/tests/functional/arch/zx81sd/math_acs.bas new file mode 100644 index 000000000..408dbedaa --- /dev/null +++ b/tests/functional/arch/zx81sd/math_acs.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = ACS(x) diff --git a/tests/functional/arch/zx81sd/math_asn.asm b/tests/functional/arch/zx81sd/math_asn.asm new file mode 100644 index 000000000..8b9444884 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_asn.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.ASIN + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/asin.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/asin.asm" + push namespace core +ASIN: ; Computes ASIN using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 22h ; ASIN + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_asn.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 16 "arch/zx81sd/math_asn.bas" + END diff --git a/tests/functional/arch/zx81sd/math_asn.bas b/tests/functional/arch/zx81sd/math_asn.bas new file mode 100644 index 000000000..0bf1ded8c --- /dev/null +++ b/tests/functional/arch/zx81sd/math_asn.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = ASN(x) diff --git a/tests/functional/arch/zx81sd/math_atn.asm b/tests/functional/arch/zx81sd/math_atn.asm new file mode 100644 index 000000000..41946b50e --- /dev/null +++ b/tests/functional/arch/zx81sd/math_atn.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.ATAN + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/atan.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/atan.asm" + push namespace core +ATAN: ; Computes ATAN using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 24h ; ATAN + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_atn.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 16 "arch/zx81sd/math_atn.bas" + END diff --git a/tests/functional/arch/zx81sd/math_atn.bas b/tests/functional/arch/zx81sd/math_atn.bas new file mode 100644 index 000000000..edeea9ce0 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_atn.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = ATN(x) diff --git a/tests/functional/arch/zx81sd/math_cos.asm b/tests/functional/arch/zx81sd/math_cos.asm new file mode 100644 index 000000000..310433164 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_cos.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.COS + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/cos.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/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 15 "arch/zx81sd/math_cos.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 16 "arch/zx81sd/math_cos.bas" + END diff --git a/tests/functional/arch/zx81sd/math_cos.bas b/tests/functional/arch/zx81sd/math_cos.bas new file mode 100644 index 000000000..ba4a826be --- /dev/null +++ b/tests/functional/arch/zx81sd/math_cos.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = COS(x) diff --git a/tests/functional/arch/zx81sd/math_exp.asm b/tests/functional/arch/zx81sd/math_exp.asm new file mode 100644 index 000000000..02293a2ff --- /dev/null +++ b/tests/functional/arch/zx81sd/math_exp.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.EXP + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/exp.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/exp.asm" + push namespace core +EXP: ; Computes e^n using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 26h ; E^n + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_exp.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 16 "arch/zx81sd/math_exp.bas" + END diff --git a/tests/functional/arch/zx81sd/math_exp.bas b/tests/functional/arch/zx81sd/math_exp.bas new file mode 100644 index 000000000..379a29899 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_exp.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = EXP(x) diff --git a/tests/functional/arch/zx81sd/math_ln.asm b/tests/functional/arch/zx81sd/math_ln.asm new file mode 100644 index 000000000..4e46e80c0 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_ln.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.LN + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/logn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/logn.asm" + push namespace core +LN: ; Computes Ln(x) using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 25h + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_ln.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 16 "arch/zx81sd/math_ln.bas" + END diff --git a/tests/functional/arch/zx81sd/math_ln.bas b/tests/functional/arch/zx81sd/math_ln.bas new file mode 100644 index 000000000..bf12598bd --- /dev/null +++ b/tests/functional/arch/zx81sd/math_ln.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = LN(x) diff --git a/tests/functional/arch/zx81sd/math_sin.asm b/tests/functional/arch/zx81sd/math_sin.asm new file mode 100644 index 000000000..274413198 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_sin.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.SIN + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sin.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + 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 + rst 28h ; ROM CALC + defb 1Fh + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_sin.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 16 "arch/zx81sd/math_sin.bas" + END diff --git a/tests/functional/arch/zx81sd/math_sin.bas b/tests/functional/arch/zx81sd/math_sin.bas new file mode 100644 index 000000000..6ff0b3997 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_sin.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = SIN(x) diff --git a/tests/functional/arch/zx81sd/math_sqr.asm b/tests/functional/arch/zx81sd/math_sqr.asm new file mode 100644 index 000000000..e956af2c1 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_sqr.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.SQRT + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/sqrt.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/sqrt.asm" + push namespace core +SQRT: ; Computes SQRT(x) using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 28h ; SQRT + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_sqr.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 16 "arch/zx81sd/math_sqr.bas" + END diff --git a/tests/functional/arch/zx81sd/math_sqr.bas b/tests/functional/arch/zx81sd/math_sqr.bas new file mode 100644 index 000000000..adbbdf141 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_sqr.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = SQR(x) diff --git a/tests/functional/arch/zx81sd/math_tan.asm b/tests/functional/arch/zx81sd/math_tan.asm new file mode 100644 index 000000000..018f69f80 --- /dev/null +++ b/tests/functional/arch/zx81sd/math_tan.asm @@ -0,0 +1,2372 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_x) + ld de, (_x + 1) + ld bc, (_x + 3) + call .core.TAN + ld hl, _x + call .core.__STOREF + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/math/tan.asm" + push namespace core +TAN: ; Computes TAN using ROM FP-CALC + call __FPSTACK_PUSH + rst 28h ; ROM CALC + defb 21h ; TAN + defb 38h ; END CALC + jp __FPSTACK_POP + pop namespace +#line 15 "arch/zx81sd/math_tan.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 16 "arch/zx81sd/math_tan.bas" + END diff --git a/tests/functional/arch/zx81sd/math_tan.bas b/tests/functional/arch/zx81sd/math_tan.bas new file mode 100644 index 000000000..e44434e1b --- /dev/null +++ b/tests/functional/arch/zx81sd/math_tan.bas @@ -0,0 +1,2 @@ +DIM x As Float +LET x = TAN(x) diff --git a/tests/functional/arch/zx81sd/modf.asm b/tests/functional/arch/zx81sd/modf.asm new file mode 100644 index 000000000..15249965d --- /dev/null +++ b/tests/functional/arch/zx81sd/modf.asm @@ -0,0 +1,2413 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/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 17 "arch/zx81sd/modf.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 18 "arch/zx81sd/modf.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 19 "arch/zx81sd/modf.bas" + END diff --git a/tests/functional/arch/zx81sd/modf.bas b/tests/functional/arch/zx81sd/modf.bas new file mode 100644 index 000000000..d5c52ff6f --- /dev/null +++ b/tests/functional/arch/zx81sd/modf.bas @@ -0,0 +1,3 @@ +DIM a as Float + +LET a = a MOD 1 diff --git a/tests/functional/arch/zx81sd/modf16.asm b/tests/functional/arch/zx81sd/modf16.asm new file mode 100644 index 000000000..e7c7a5364 --- /dev/null +++ b/tests/functional/arch/zx81sd/modf16.asm @@ -0,0 +1,81 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/modf16.bas b/tests/functional/arch/zx81sd/modf16.bas new file mode 100644 index 000000000..491bf7b5d --- /dev/null +++ b/tests/functional/arch/zx81sd/modf16.bas @@ -0,0 +1,3 @@ +DIM a as Fixed + +LET a = a MOD 1 diff --git a/tests/functional/arch/zx81sd/modf16c.asm b/tests/functional/arch/zx81sd/modf16c.asm new file mode 100644 index 000000000..daea0623b --- /dev/null +++ b/tests/functional/arch/zx81sd/modf16c.asm @@ -0,0 +1,484 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/modf16.asm" + ; Computes A % B for fixed values +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/divf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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/zx48k/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/zx48k/runtime/arith/modf16.asm" +#line 1 "/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 5 "/zxbasic/src/lib/arch/zx48k/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 55 "arch/zx81sd/modf16c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 56 "arch/zx81sd/modf16c.bas" + END diff --git a/tests/functional/arch/zx81sd/modf16c.bas b/tests/functional/arch/zx81sd/modf16c.bas new file mode 100644 index 000000000..5437b62a5 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modi32c.asm b/tests/functional/arch/zx81sd/modi32c.asm new file mode 100644 index 000000000..eb7ad7a54 --- /dev/null +++ b/tests/functional/arch/zx81sd/modi32c.asm @@ -0,0 +1,291 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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 58 "arch/zx81sd/modi32c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 59 "arch/zx81sd/modi32c.bas" + END diff --git a/tests/functional/arch/zx81sd/modi32c.bas b/tests/functional/arch/zx81sd/modi32c.bas new file mode 100644 index 000000000..1b2b018a4 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modi8.asm b/tests/functional/arch/zx81sd/modi8.asm new file mode 100644 index 000000000..bb6a56093 --- /dev/null +++ b/tests/functional/arch/zx81sd/modi8.asm @@ -0,0 +1,168 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 35 "arch/zx81sd/modi8.bas" + END diff --git a/tests/functional/arch/zx81sd/modi8.bas b/tests/functional/arch/zx81sd/modi8.bas new file mode 100644 index 000000000..df2aad4f7 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modi8a.asm b/tests/functional/arch/zx81sd/modi8a.asm new file mode 100644 index 000000000..1eb093bb0 --- /dev/null +++ b/tests/functional/arch/zx81sd/modi8a.asm @@ -0,0 +1,146 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 15 "arch/zx81sd/modi8a.bas" + END diff --git a/tests/functional/arch/zx81sd/modi8a.bas b/tests/functional/arch/zx81sd/modi8a.bas new file mode 100644 index 000000000..9d0447303 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modi8b.asm b/tests/functional/arch/zx81sd/modi8b.asm new file mode 100644 index 000000000..13ddc296f --- /dev/null +++ b/tests/functional/arch/zx81sd/modi8b.asm @@ -0,0 +1,152 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 21 "arch/zx81sd/modi8b.bas" + END diff --git a/tests/functional/arch/zx81sd/modi8b.bas b/tests/functional/arch/zx81sd/modi8b.bas new file mode 100644 index 000000000..e23eed1ca --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modu32c.asm b/tests/functional/arch/zx81sd/modu32c.asm new file mode 100644 index 000000000..c712bbc11 --- /dev/null +++ b/tests/functional/arch/zx81sd/modu32c.asm @@ -0,0 +1,291 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/div32.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/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 58 "arch/zx81sd/modu32c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 59 "arch/zx81sd/modu32c.bas" + END diff --git a/tests/functional/arch/zx81sd/modu32c.bas b/tests/functional/arch/zx81sd/modu32c.bas new file mode 100644 index 000000000..1bcaebc3f --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modu8.asm b/tests/functional/arch/zx81sd/modu8.asm new file mode 100644 index 000000000..4a95aa3ff --- /dev/null +++ b/tests/functional/arch/zx81sd/modu8.asm @@ -0,0 +1,168 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 35 "arch/zx81sd/modu8.bas" + END diff --git a/tests/functional/arch/zx81sd/modu8.bas b/tests/functional/arch/zx81sd/modu8.bas new file mode 100644 index 000000000..fb8777395 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modu8a.asm b/tests/functional/arch/zx81sd/modu8a.asm new file mode 100644 index 000000000..74500e7fb --- /dev/null +++ b/tests/functional/arch/zx81sd/modu8a.asm @@ -0,0 +1,146 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 15 "arch/zx81sd/modu8a.bas" + END diff --git a/tests/functional/arch/zx81sd/modu8a.bas b/tests/functional/arch/zx81sd/modu8a.bas new file mode 100644 index 000000000..5b8038ede --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/modu8b.asm b/tests/functional/arch/zx81sd/modu8b.asm new file mode 100644 index 000000000..a5998e842 --- /dev/null +++ b/tests/functional/arch/zx81sd/modu8b.asm @@ -0,0 +1,152 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/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 21 "arch/zx81sd/modu8b.bas" + END diff --git a/tests/functional/arch/zx81sd/modu8b.bas b/tests/functional/arch/zx81sd/modu8b.bas new file mode 100644 index 000000000..0cfe81f02 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mul16.asm b/tests/functional/arch/zx81sd/mul16.asm new file mode 100644 index 000000000..b4b940d52 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul16.asm @@ -0,0 +1,115 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 21 "arch/zx81sd/mul16.bas" + END diff --git a/tests/functional/arch/zx81sd/mul16.bas b/tests/functional/arch/zx81sd/mul16.bas new file mode 100644 index 000000000..8cdb72ac1 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mul16a.asm b/tests/functional/arch/zx81sd/mul16a.asm new file mode 100644 index 000000000..917ca5457 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul16a.asm @@ -0,0 +1,108 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 16 "arch/zx81sd/mul16a.bas" + END diff --git a/tests/functional/arch/zx81sd/mul16a.bas b/tests/functional/arch/zx81sd/mul16a.bas new file mode 100644 index 000000000..15611ed0b --- /dev/null +++ b/tests/functional/arch/zx81sd/mul16a.bas @@ -0,0 +1,4 @@ + +REM another MUL16 test +DIM a As Integer +a = a * a * a diff --git a/tests/functional/arch/zx81sd/mul16b.asm b/tests/functional/arch/zx81sd/mul16b.asm new file mode 100644 index 000000000..f2b026c40 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul16b.asm @@ -0,0 +1,114 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 22 "arch/zx81sd/mul16b.bas" + END diff --git a/tests/functional/arch/zx81sd/mul16b.bas b/tests/functional/arch/zx81sd/mul16b.bas new file mode 100644 index 000000000..1d849dff4 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mul16c.asm b/tests/functional/arch/zx81sd/mul16c.asm new file mode 100644 index 000000000..f2066148d --- /dev/null +++ b/tests/functional/arch/zx81sd/mul16c.asm @@ -0,0 +1,105 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 13 "arch/zx81sd/mul16c.bas" + END diff --git a/tests/functional/arch/zx81sd/mul16c.bas b/tests/functional/arch/zx81sd/mul16c.bas new file mode 100644 index 000000000..6462127b8 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul16c.bas @@ -0,0 +1,3 @@ +DIM a as Integer + +a = -1 * a diff --git a/tests/functional/arch/zx81sd/mul32.asm b/tests/functional/arch/zx81sd/mul32.asm new file mode 100644 index 000000000..e878b8118 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32.asm @@ -0,0 +1,115 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 21 "arch/zx81sd/mul32.bas" + END diff --git a/tests/functional/arch/zx81sd/mul32.bas b/tests/functional/arch/zx81sd/mul32.bas new file mode 100644 index 000000000..8cdb72ac1 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32.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/zx81sd/mul32a.asm b/tests/functional/arch/zx81sd/mul32a.asm new file mode 100644 index 000000000..e629e41e2 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32a.asm @@ -0,0 +1,108 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 16 "arch/zx81sd/mul32a.bas" + END diff --git a/tests/functional/arch/zx81sd/mul32a.bas b/tests/functional/arch/zx81sd/mul32a.bas new file mode 100644 index 000000000..15611ed0b --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32a.bas @@ -0,0 +1,4 @@ + +REM another MUL16 test +DIM a As Integer +a = a * a * a diff --git a/tests/functional/arch/zx81sd/mul32b.asm b/tests/functional/arch/zx81sd/mul32b.asm new file mode 100644 index 000000000..ef3b81570 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32b.asm @@ -0,0 +1,114 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 22 "arch/zx81sd/mul32b.bas" + END diff --git a/tests/functional/arch/zx81sd/mul32b.bas b/tests/functional/arch/zx81sd/mul32b.bas new file mode 100644 index 000000000..1d849dff4 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32b.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/zx81sd/mul32c.asm b/tests/functional/arch/zx81sd/mul32c.asm new file mode 100644 index 000000000..30ed1b985 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32c.asm @@ -0,0 +1,105 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- 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 13 "arch/zx81sd/mul32c.bas" + END diff --git a/tests/functional/arch/zx81sd/mul32c.bas b/tests/functional/arch/zx81sd/mul32c.bas new file mode 100644 index 000000000..6462127b8 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul32c.bas @@ -0,0 +1,3 @@ +DIM a as Integer + +a = -1 * a diff --git a/tests/functional/arch/zx81sd/mul8.asm b/tests/functional/arch/zx81sd/mul8.asm new file mode 100644 index 000000000..9ff0873e2 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul8.asm @@ -0,0 +1,151 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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) + call .core.__MUL8_FAST + ld (_b), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- 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 37 "arch/zx81sd/mul8.bas" + END diff --git a/tests/functional/arch/zx81sd/mul8.bas b/tests/functional/arch/zx81sd/mul8.bas new file mode 100644 index 000000000..90671ea95 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mul8a.asm b/tests/functional/arch/zx81sd/mul8a.asm new file mode 100644 index 000000000..4a87baaad --- /dev/null +++ b/tests/functional/arch/zx81sd/mul8a.asm @@ -0,0 +1,128 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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) + call .core.__MUL8_FAST + ld h, a + ld a, (_a) + call .core.__MUL8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- 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 16 "arch/zx81sd/mul8a.bas" + END diff --git a/tests/functional/arch/zx81sd/mul8a.bas b/tests/functional/arch/zx81sd/mul8a.bas new file mode 100644 index 000000000..cbe63126e --- /dev/null +++ b/tests/functional/arch/zx81sd/mul8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a * a * a diff --git a/tests/functional/arch/zx81sd/mul8b.asm b/tests/functional/arch/zx81sd/mul8b.asm new file mode 100644 index 000000000..6371770d8 --- /dev/null +++ b/tests/functional/arch/zx81sd/mul8b.asm @@ -0,0 +1,134 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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) + call .core.__MUL8_FAST + push af + ld a, (_a) + add a, a + ld h, a + ld a, (_a) + call .core.__MUL8_FAST + ld h, a + pop af + call .core.__MUL8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- 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 22 "arch/zx81sd/mul8b.bas" + END diff --git a/tests/functional/arch/zx81sd/mul8b.bas b/tests/functional/arch/zx81sd/mul8b.bas new file mode 100644 index 000000000..11c8da54d --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mulf00.asm b/tests/functional/arch/zx81sd/mulf00.asm new file mode 100644 index 000000000..ce5731c28 --- /dev/null +++ b/tests/functional/arch/zx81sd/mulf00.asm @@ -0,0 +1,2421 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + 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 17 "arch/zx81sd/mulf00.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 18 "arch/zx81sd/mulf00.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 19 "arch/zx81sd/mulf00.bas" + END diff --git a/tests/functional/arch/zx81sd/mulf00.bas b/tests/functional/arch/zx81sd/mulf00.bas new file mode 100644 index 000000000..57211f66c --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mulf01.asm b/tests/functional/arch/zx81sd/mulf01.asm new file mode 100644 index 000000000..c02b151bd --- /dev/null +++ b/tests/functional/arch/zx81sd/mulf01.asm @@ -0,0 +1,2397 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/mulf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + 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 21 "arch/zx81sd/mulf01.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 22 "arch/zx81sd/mulf01.bas" + END diff --git a/tests/functional/arch/zx81sd/mulf01.bas b/tests/functional/arch/zx81sd/mulf01.bas new file mode 100644 index 000000000..95ab7e941 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mulf16.asm b/tests/functional/arch/zx81sd/mulf16.asm new file mode 100644 index 000000000..14f4272b6 --- /dev/null +++ b/tests/functional/arch/zx81sd/mulf16.asm @@ -0,0 +1,229 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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 40 "arch/zx81sd/mulf16.bas" + END diff --git a/tests/functional/arch/zx81sd/mulf16.bas b/tests/functional/arch/zx81sd/mulf16.bas new file mode 100644 index 000000000..06cc5323c --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/mulf16a.asm b/tests/functional/arch/zx81sd/mulf16a.asm new file mode 100644 index 000000000..d5f5a1f22 --- /dev/null +++ b/tests/functional/arch/zx81sd/mulf16a.asm @@ -0,0 +1,213 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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 18 "arch/zx81sd/mulf16a.bas" + END diff --git a/tests/functional/arch/zx81sd/mulf16a.bas b/tests/functional/arch/zx81sd/mulf16a.bas new file mode 100644 index 000000000..ed503c75f --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/muli8.asm b/tests/functional/arch/zx81sd/muli8.asm new file mode 100644 index 000000000..585186606 --- /dev/null +++ b/tests/functional/arch/zx81sd/muli8.asm @@ -0,0 +1,125 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 h, 3 + call .core.__MUL8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- 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 13 "arch/zx81sd/muli8.bas" + END diff --git a/tests/functional/arch/zx81sd/muli8.bas b/tests/functional/arch/zx81sd/muli8.bas new file mode 100644 index 000000000..f474773d8 --- /dev/null +++ b/tests/functional/arch/zx81sd/muli8.bas @@ -0,0 +1,3 @@ +DIM a as Byte + +LET a = a * 3 diff --git a/tests/functional/arch/zx81sd/mulu16.asm b/tests/functional/arch/zx81sd/mulu16.asm new file mode 100644 index 000000000..914f71f34 --- /dev/null +++ b/tests/functional/arch/zx81sd/mulu16.asm @@ -0,0 +1,105 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, 3 + call .core.__MUL16_FAST + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- 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 13 "arch/zx81sd/mulu16.bas" + END diff --git a/tests/functional/arch/zx81sd/mulu16.bas b/tests/functional/arch/zx81sd/mulu16.bas new file mode 100644 index 000000000..64371df4d --- /dev/null +++ b/tests/functional/arch/zx81sd/mulu16.bas @@ -0,0 +1,3 @@ +DIM a as UInteger + +LET a = a * 3 diff --git a/tests/functional/arch/zx81sd/mulu8.asm b/tests/functional/arch/zx81sd/mulu8.asm new file mode 100644 index 000000000..6e1459f70 --- /dev/null +++ b/tests/functional/arch/zx81sd/mulu8.asm @@ -0,0 +1,125 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 h, 3 + call .core.__MUL8_FAST + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- 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 13 "arch/zx81sd/mulu8.bas" + END diff --git a/tests/functional/arch/zx81sd/mulu8.bas b/tests/functional/arch/zx81sd/mulu8.bas new file mode 100644 index 000000000..18c7f4d79 --- /dev/null +++ b/tests/functional/arch/zx81sd/mulu8.bas @@ -0,0 +1,3 @@ +DIM a as UByte + +LET a = a * 3 diff --git a/tests/functional/arch/zx81sd/nef.asm b/tests/functional/arch/zx81sd/nef.asm new file mode 100644 index 000000000..c67253eb7 --- /dev/null +++ b/tests/functional/arch/zx81sd/nef.asm @@ -0,0 +1,2553 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, _t + 4 + call .core.__FP_PUSH_REV + ld a, 000h + ld de, 00000h + ld bc, 00000h + call .core.__NEF + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/nef.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 + 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/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/zx48k/runtime/cmp/nef.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) + ; 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/zx48k/runtime/cmp/nef.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 4 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/nef.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 +__NEF: ; A <> B + call __FPSTACK_PUSH2 ; Enters B, A + ; ------------- ROM NOS-NEQ + ld b, 0Bh + rst 28h + defb 0Bh + defb 38h ; END CALC + call __FPSTACK_POP + jp __FTOU8 ; Convert to 8 bits + pop namespace +#line 16 "arch/zx81sd/nef.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 17 "arch/zx81sd/nef.bas" + END diff --git a/tests/functional/arch/zx81sd/nef.bas b/tests/functional/arch/zx81sd/nef.bas new file mode 100644 index 000000000..a21c48dcc --- /dev/null +++ b/tests/functional/arch/zx81sd/nef.bas @@ -0,0 +1,3 @@ +DIM t as Float + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/nef16.asm b/tests/functional/arch/zx81sd/nef16.asm new file mode 100644 index 000000000..ee6974a5a --- /dev/null +++ b/tests/functional/arch/zx81sd/nef16.asm @@ -0,0 +1,111 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_t + 2) + push hl + ld hl, (_t) + push hl + ld de, 0 + ld hl, 0 + call .core.__EQ32 + sub 1 + sbc a, a + neg + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/eq32.asm" + push namespace core +__EQ32: ; Test if 32bit value HLDE equals top of the stack + ; Returns result in A: 0 = False, FF = True + exx + pop bc ; Return address + exx + xor a ; Reset carry flag + pop bc + sbc hl, bc ; Low part + ex de, hl + pop bc + sbc hl, bc ; High part + exx + push bc ; CALLEE + exx + ld a, h + or l + or d + or e ; a = 0 and Z flag set only if HLDE = 0 + ld a, 1 + ret z + xor a + ret + pop namespace +#line 20 "arch/zx81sd/nef16.bas" + END diff --git a/tests/functional/arch/zx81sd/nef16.bas b/tests/functional/arch/zx81sd/nef16.bas new file mode 100644 index 000000000..0da3f292b --- /dev/null +++ b/tests/functional/arch/zx81sd/nef16.bas @@ -0,0 +1,3 @@ +DIM t as Fixed + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/nei16.asm b/tests/functional/arch/zx81sd/nei16.asm new file mode 100644 index 000000000..5593c7177 --- /dev/null +++ b/tests/functional/arch/zx81sd/nei16.asm @@ -0,0 +1,84 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, 0 + ld hl, (_t) + or a + sbc hl, de + ld a, h + or l + sub 1 + sbc a, a + inc a + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/nei16.bas b/tests/functional/arch/zx81sd/nei16.bas new file mode 100644 index 000000000..bf55a4674 --- /dev/null +++ b/tests/functional/arch/zx81sd/nei16.bas @@ -0,0 +1,3 @@ +DIM t as Integer + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/nei32.asm b/tests/functional/arch/zx81sd/nei32.asm new file mode 100644 index 000000000..1cd945bdc --- /dev/null +++ b/tests/functional/arch/zx81sd/nei32.asm @@ -0,0 +1,111 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_t + 2) + push hl + ld hl, (_t) + push hl + ld de, 0 + ld hl, 0 + call .core.__EQ32 + sub 1 + sbc a, a + neg + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/eq32.asm" + push namespace core +__EQ32: ; Test if 32bit value HLDE equals top of the stack + ; Returns result in A: 0 = False, FF = True + exx + pop bc ; Return address + exx + xor a ; Reset carry flag + pop bc + sbc hl, bc ; Low part + ex de, hl + pop bc + sbc hl, bc ; High part + exx + push bc ; CALLEE + exx + ld a, h + or l + or d + or e ; a = 0 and Z flag set only if HLDE = 0 + ld a, 1 + ret z + xor a + ret + pop namespace +#line 20 "arch/zx81sd/nei32.bas" + END diff --git a/tests/functional/arch/zx81sd/nei32.bas b/tests/functional/arch/zx81sd/nei32.bas new file mode 100644 index 000000000..2ecac569e --- /dev/null +++ b/tests/functional/arch/zx81sd/nei32.bas @@ -0,0 +1,3 @@ +DIM t as Long + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/nei8.asm b/tests/functional/arch/zx81sd/nei8.asm new file mode 100644 index 000000000..511c873a3 --- /dev/null +++ b/tests/functional/arch/zx81sd/nei8.asm @@ -0,0 +1,79 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_t) + sub 1 + sbc a, a + inc a + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/nei8.bas b/tests/functional/arch/zx81sd/nei8.bas new file mode 100644 index 000000000..404cf6b91 --- /dev/null +++ b/tests/functional/arch/zx81sd/nei8.bas @@ -0,0 +1,3 @@ +DIM t as Byte + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/neu16.asm b/tests/functional/arch/zx81sd/neu16.asm new file mode 100644 index 000000000..5593c7177 --- /dev/null +++ b/tests/functional/arch/zx81sd/neu16.asm @@ -0,0 +1,84 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, 0 + ld hl, (_t) + or a + sbc hl, de + ld a, h + or l + sub 1 + sbc a, a + inc a + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/neu16.bas b/tests/functional/arch/zx81sd/neu16.bas new file mode 100644 index 000000000..dec3e6617 --- /dev/null +++ b/tests/functional/arch/zx81sd/neu16.bas @@ -0,0 +1,3 @@ +DIM t as UInteger + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/neu32.asm b/tests/functional/arch/zx81sd/neu32.asm new file mode 100644 index 000000000..22a067fac --- /dev/null +++ b/tests/functional/arch/zx81sd/neu32.asm @@ -0,0 +1,111 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_t + 2) + push hl + ld hl, (_t) + push hl + ld de, 0 + ld hl, 0 + call .core.__EQ32 + sub 1 + sbc a, a + neg + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/eq32.asm" + push namespace core +__EQ32: ; Test if 32bit value HLDE equals top of the stack + ; Returns result in A: 0 = False, FF = True + exx + pop bc ; Return address + exx + xor a ; Reset carry flag + pop bc + sbc hl, bc ; Low part + ex de, hl + pop bc + sbc hl, bc ; High part + exx + push bc ; CALLEE + exx + ld a, h + or l + or d + or e ; a = 0 and Z flag set only if HLDE = 0 + ld a, 1 + ret z + xor a + ret + pop namespace +#line 20 "arch/zx81sd/neu32.bas" + END diff --git a/tests/functional/arch/zx81sd/neu32.bas b/tests/functional/arch/zx81sd/neu32.bas new file mode 100644 index 000000000..6abac5472 --- /dev/null +++ b/tests/functional/arch/zx81sd/neu32.bas @@ -0,0 +1,3 @@ +DIM t as Ulong + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/neu8.asm b/tests/functional/arch/zx81sd/neu8.asm new file mode 100644 index 000000000..511c873a3 --- /dev/null +++ b/tests/functional/arch/zx81sd/neu8.asm @@ -0,0 +1,79 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_t: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_t) + sub 1 + sbc a, a + inc a + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/neu8.bas b/tests/functional/arch/zx81sd/neu8.bas new file mode 100644 index 000000000..75cb922d1 --- /dev/null +++ b/tests/functional/arch/zx81sd/neu8.bas @@ -0,0 +1,3 @@ +DIM t as UByte + +POKE 0, (t <> 0) diff --git a/tests/functional/arch/zx81sd/opt1_beep_const.asm b/tests/functional/arch/zx81sd/opt1_beep_const.asm new file mode 100644 index 000000000..b37233154 --- /dev/null +++ b/tests/functional/arch/zx81sd/opt1_beep_const.asm @@ -0,0 +1,233 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 1642 + push hl + ld hl, 261 + call .core.__BEEPER + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm" + ; beeper.asm (zx81sd) — Generador de onda cuadrada por el beeper del SD81 + ; + ; Sustituye a zx48k/runtime/io/sound/beeper.asm, que llama a BEEPER ($03B5) +; de la ROM Spectrum. Aqui se porta el bucle original de la ROM adaptando: + ; +; - Puerto: $FB (ULA emulada del SD81 en modo HiRes Spectrum) en vez de +; $FE. Mismo formato: bits 2-0 = borde, bit 3 = MIC, bit 4 = altavoz. + ; El borde se toma de la copia sombra __ZX81SD_ULA_SHADOW (beep.asm) + ; en vez de BORDCR ($5C48), y se restaura al terminar. +; - Reloj: el bucle es identico en T-states (no depende del reloj), pero + ; el periodo HL que llega del COMPILADOR (BEEP const,const se precalcula +; en el frontend con la formula del Spectrum: HL = 437500/f - 30.125, +; 437500 = 3.5MHz/8) hay que corregirlo para los 3.25 MHz del ZX81: + ; HL' = 406250/f - 30.125 = HL*13/14 - 2.15 (406250/437500 = 13/14) + ; La entrada __BEEPER (la que usa el compilador) aplica esa correccion. + ; La entrada __ZX81SD_BEEPER (interna) recibe el periodo ya en unidades + ; de 3.25 MHz y no corrige nada (la usa nuestro BEEP de beep.asm, que + ; calcula directamente con 406250). +; - Interrupciones: la ROM hace DI...EI. El runtime zx81sd se ejecuta con + ; las interrupciones deshabilitadas (la FPGA genera el video), asi que + ; se mantiene el DI (inocuo) y NO se hace EI al final. + ; +; __ZX81SD_BEEPER — entrada directa (formato ROM $03B5): + ; DE = numero de ciclos - 1 +; HL = periodo del tono: T-states = 236 + 8*HL (a 3.25 MHz) +; Modifica: AF, BC, DE, HL, IX (el llamador debe preservar IX si lo usa) +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/beep.asm" + ; BEEP — Pulso corto de beeper (clic de tecla) + ; + ; SD81 Booster en modo Superfast HiRes Spectrum emula el puerto ULA del +; Spectrum en $FBh: bits 2-0 = color de borde, bits 4-3 = beeper (ver + ; border.asm y Apendice "Modo Spectrum" del manual del SD81 Booster). + ; Ambas funciones comparten el mismo puerto de solo escritura, asi que + ; se mantiene una copia sombra del ultimo byte escrito para poder + ; pulsar el beeper sin alterar el color de borde actual (y viceversa). + push namespace core + SD81_ULA_PORT EQU $FB ; Puerto ULA emulado en modo HiRes Spectrum + SD81_ULA_BEEP_BITS EQU $18 ; bits 4-3 +__ZX81SD_ULA_SHADOW: + DEFB 0 + ; --------------------------------------------------------------------------- + ; __ZX81SD_KEYCLICK — Pulso breve de beeper (clic de tecla) +; No recibe ni devuelve nada. Registros modificados: AF, BC. + ; --------------------------------------------------------------------------- +__ZX81SD_KEYCLICK: + PROC + LOCAL CLICK_DELAY + ld hl, __ZX81SD_ULA_SHADOW + ld a, (hl) + or SD81_ULA_BEEP_BITS + out (SD81_ULA_PORT), a + ld b, 30 +CLICK_DELAY: + djnz CLICK_DELAY + ld a, (hl) ; restaura el byte previo (beeper apagado, borde intacto) + out (SD81_ULA_PORT), a + ret + ENDP + pop namespace +#line 29 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm" + push namespace core +__ZX81SD_BEEPER: + PROC + LOCAL BE_IX3, BE_IX2, BE_IX1, BE_IX0 + LOCAL BE_HL_LP, BE_AGAIN, BE_END + di ; timing exacto (ya suelen estar deshabilitadas) + ld a, l + srl l + srl l ; L = parte media del periodo + cpl + and $03 ; A = 3 - parte fina del periodo + ld c, a + ld b, $00 + ld ix, BE_IX3 + add ix, bc ; IX = entrada al bucle con 0-3 NOPs (parte fina) + ld a, (__ZX81SD_ULA_SHADOW) + and $07 ; bits 2-0 = borde actual + or $08 ; bit 3 (MIC) a 1, como la ROM +BE_IX3: + nop ;(4) NOPs opcionales: ajuste fino del periodo +BE_IX2: + nop ;(4) +BE_IX1: + nop ;(4) +BE_IX0: + inc b ;(4) + inc c ;(4) +BE_HL_LP: + dec c ;(4) bucle de duracion del semiciclo + jr nz, BE_HL_LP ;(12/7) + ld c, $3F ;(7) + dec b ;(4) + jp nz, BE_HL_LP ;(10) + xor $10 ;(7) conmuta el bit del altavoz + out (SD81_ULA_PORT), a ;(11) + ld b, h ;(4) B = parte gruesa del periodo + ld c, a ;(4) salva el byte del puerto + bit 4, a ;(8) si la salida quedo alta, + jr nz, BE_AGAIN ;(12/7) hace el semiciclo alto + ld a, d ;(4) ciclo completo (bajo->bajo): + or e ;(4) ¿quedan ciclos? + jr z, BE_END ;(12/7) + ld a, c ;(4) restaura el byte del puerto + ld c, l ;(4) C = parte media del periodo + dec de ;(6) + jp (ix) ;(8) siguiente ciclo +BE_AGAIN: ; a mitad de ciclo + ld c, l ;(4) + inc c ;(4) +16 T para igualar semiciclo alto y bajo + jp (ix) ;(8) +BE_END: + ld a, (__ZX81SD_ULA_SHADOW) + out (SD81_ULA_PORT), a ; deja borde/beeper como estaban (sin EI: ver cabecera) + ret + ENDP + ; --------------------------------------------------------------------------- + ; __BEEPER — Entrada del compilador para BEEP , + ; HL (fastcall) = numero de ciclos - 1 + ; (SP+2) en la pila = periodo calculado por el frontend PARA 3.5 MHz + ; Corrige el periodo a 3.25 MHz (HL' = HL - HL/14 - 2) y llama al bucle. + ; --------------------------------------------------------------------------- +__BEEPER: + PROC + LOCAL DIV14, DIV14_SKIP + ex de, hl ; DE = ciclos - 1 + pop hl ; direccion de retorno + ex (sp), hl ; HL = periodo (unidades Spectrum) — CALLEE + push de ; salva ciclos + push hl ; salva periodo original + ex de, hl ; DE = dividendo (periodo) + xor a ; A = resto + ld c, 14 + ld b, 16 +DIV14: + sla e ; desplaza el dividendo (bit 0 entra a 0; + rl d ; NO usar rl e: arrastraria el acarreo residual + rla ; del cp/sub de la vuelta anterior) + cp c + jr c, DIV14_SKIP + sub c + inc e +DIV14_SKIP: + djnz DIV14 ; DE = periodo / 14 + pop hl ; periodo original + or a + sbc hl, de ; HL = periodo - periodo/14 (= periodo*13/14) + dec hl + dec hl ; -2 (~ -30.125*(1-13/14), redondeado) + pop de ; ciclos - 1 + push ix ; el bucle usa IX; el compilador usa IX como frame + call __ZX81SD_BEEPER + pop ix + ret + ENDP + pop namespace +#line 13 "arch/zx81sd/opt1_beep_const.bas" + END diff --git a/tests/functional/arch/zx81sd/opt1_beep_const.bas b/tests/functional/arch/zx81sd/opt1_beep_const.bas new file mode 100644 index 000000000..4848d04dc --- /dev/null +++ b/tests/functional/arch/zx81sd/opt1_beep_const.bas @@ -0,0 +1,3 @@ +' Beep with constants + +BEEP 1, 0.0 diff --git a/tests/functional/arch/zx81sd/opt1_beep_var.asm b/tests/functional/arch/zx81sd/opt1_beep_var.asm new file mode 100644 index 000000000..7c15c4f3c --- /dev/null +++ b/tests/functional/arch/zx81sd/opt1_beep_var.asm @@ -0,0 +1,2769 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 000h + ld de, 00000h + ld bc, 00000h + push bc + push de + push af + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + call .core.BEEP + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beep.asm" + ; beep.asm (zx81sd) — Comando BEEP duracion, tono (version con expresiones) + ; + ; Sustituye a zx48k/runtime/io/sound/beep.asm, que llama a BEEP ($03F8) de + ; la ROM Spectrum. Aqui se porta la rutina original usando el calculador FP + ; propio (fp_calc.asm) y el bucle de beeper propio (io/sound/beeper.asm). + ; + ; BEEP dur, pitch + ; dur = duracion en segundos (0 a 10) + ; pitch = semitonos sobre/bajo el DO central (-60 a 127) + ; +; Diferencias con la ROM: + ; - MEM-0 esta en FP_MEM_AREA (sysvars propias), no en $5C92. + ; - FIND-INT1/FIND-INT2 se sustituyen por __FPSTACK_POP + __FTOU32REG + ; (ya portados y verificados en zx81sd). + ; - La constante 437500 (= 3.5MHz/8 del Spectrum) se sustituye por + ; 406250 (= 3.25MHz/8 del ZX81). El resto del bytecode es identico. + ; +; Entrada (convencion del compilador, igual que en zx48k): + ; Duracion en A,E,D,C,B (float); tono en la pila de maquina (float). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 22 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beep.asm" +#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 + 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/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 25 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beep.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm" + ; beeper.asm (zx81sd) — Generador de onda cuadrada por el beeper del SD81 + ; + ; Sustituye a zx48k/runtime/io/sound/beeper.asm, que llama a BEEPER ($03B5) +; de la ROM Spectrum. Aqui se porta el bucle original de la ROM adaptando: + ; +; - Puerto: $FB (ULA emulada del SD81 en modo HiRes Spectrum) en vez de +; $FE. Mismo formato: bits 2-0 = borde, bit 3 = MIC, bit 4 = altavoz. + ; El borde se toma de la copia sombra __ZX81SD_ULA_SHADOW (beep.asm) + ; en vez de BORDCR ($5C48), y se restaura al terminar. +; - Reloj: el bucle es identico en T-states (no depende del reloj), pero + ; el periodo HL que llega del COMPILADOR (BEEP const,const se precalcula +; en el frontend con la formula del Spectrum: HL = 437500/f - 30.125, +; 437500 = 3.5MHz/8) hay que corregirlo para los 3.25 MHz del ZX81: + ; HL' = 406250/f - 30.125 = HL*13/14 - 2.15 (406250/437500 = 13/14) + ; La entrada __BEEPER (la que usa el compilador) aplica esa correccion. + ; La entrada __ZX81SD_BEEPER (interna) recibe el periodo ya en unidades + ; de 3.25 MHz y no corrige nada (la usa nuestro BEEP de beep.asm, que + ; calcula directamente con 406250). +; - Interrupciones: la ROM hace DI...EI. El runtime zx81sd se ejecuta con + ; las interrupciones deshabilitadas (la FPGA genera el video), asi que + ; se mantiene el DI (inocuo) y NO se hace EI al final. + ; +; __ZX81SD_BEEPER — entrada directa (formato ROM $03B5): + ; DE = numero de ciclos - 1 +; HL = periodo del tono: T-states = 236 + 8*HL (a 3.25 MHz) +; Modifica: AF, BC, DE, HL, IX (el llamador debe preservar IX si lo usa) +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/beep.asm" + ; BEEP — Pulso corto de beeper (clic de tecla) + ; + ; SD81 Booster en modo Superfast HiRes Spectrum emula el puerto ULA del +; Spectrum en $FBh: bits 2-0 = color de borde, bits 4-3 = beeper (ver + ; border.asm y Apendice "Modo Spectrum" del manual del SD81 Booster). + ; Ambas funciones comparten el mismo puerto de solo escritura, asi que + ; se mantiene una copia sombra del ultimo byte escrito para poder + ; pulsar el beeper sin alterar el color de borde actual (y viceversa). + push namespace core + SD81_ULA_PORT EQU $FB ; Puerto ULA emulado en modo HiRes Spectrum + SD81_ULA_BEEP_BITS EQU $18 ; bits 4-3 +__ZX81SD_ULA_SHADOW: + DEFB 0 + ; --------------------------------------------------------------------------- + ; __ZX81SD_KEYCLICK — Pulso breve de beeper (clic de tecla) +; No recibe ni devuelve nada. Registros modificados: AF, BC. + ; --------------------------------------------------------------------------- +__ZX81SD_KEYCLICK: + PROC + LOCAL CLICK_DELAY + ld hl, __ZX81SD_ULA_SHADOW + ld a, (hl) + or SD81_ULA_BEEP_BITS + out (SD81_ULA_PORT), a + ld b, 30 +CLICK_DELAY: + djnz CLICK_DELAY + ld a, (hl) ; restaura el byte previo (beeper apagado, borde intacto) + out (SD81_ULA_PORT), a + ret + ENDP + pop namespace +#line 29 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beeper.asm" + push namespace core +__ZX81SD_BEEPER: + PROC + LOCAL BE_IX3, BE_IX2, BE_IX1, BE_IX0 + LOCAL BE_HL_LP, BE_AGAIN, BE_END + di ; timing exacto (ya suelen estar deshabilitadas) + ld a, l + srl l + srl l ; L = parte media del periodo + cpl + and $03 ; A = 3 - parte fina del periodo + ld c, a + ld b, $00 + ld ix, BE_IX3 + add ix, bc ; IX = entrada al bucle con 0-3 NOPs (parte fina) + ld a, (__ZX81SD_ULA_SHADOW) + and $07 ; bits 2-0 = borde actual + or $08 ; bit 3 (MIC) a 1, como la ROM +BE_IX3: + nop ;(4) NOPs opcionales: ajuste fino del periodo +BE_IX2: + nop ;(4) +BE_IX1: + nop ;(4) +BE_IX0: + inc b ;(4) + inc c ;(4) +BE_HL_LP: + dec c ;(4) bucle de duracion del semiciclo + jr nz, BE_HL_LP ;(12/7) + ld c, $3F ;(7) + dec b ;(4) + jp nz, BE_HL_LP ;(10) + xor $10 ;(7) conmuta el bit del altavoz + out (SD81_ULA_PORT), a ;(11) + ld b, h ;(4) B = parte gruesa del periodo + ld c, a ;(4) salva el byte del puerto + bit 4, a ;(8) si la salida quedo alta, + jr nz, BE_AGAIN ;(12/7) hace el semiciclo alto + ld a, d ;(4) ciclo completo (bajo->bajo): + or e ;(4) ¿quedan ciclos? + jr z, BE_END ;(12/7) + ld a, c ;(4) restaura el byte del puerto + ld c, l ;(4) C = parte media del periodo + dec de ;(6) + jp (ix) ;(8) siguiente ciclo +BE_AGAIN: ; a mitad de ciclo + ld c, l ;(4) + inc c ;(4) +16 T para igualar semiciclo alto y bajo + jp (ix) ;(8) +BE_END: + ld a, (__ZX81SD_ULA_SHADOW) + out (SD81_ULA_PORT), a ; deja borde/beeper como estaban (sin EI: ver cabecera) + ret + ENDP + ; --------------------------------------------------------------------------- + ; __BEEPER — Entrada del compilador para BEEP , + ; HL (fastcall) = numero de ciclos - 1 + ; (SP+2) en la pila = periodo calculado por el frontend PARA 3.5 MHz + ; Corrige el periodo a 3.25 MHz (HL' = HL - HL/14 - 2) y llama al bucle. + ; --------------------------------------------------------------------------- +__BEEPER: + PROC + LOCAL DIV14, DIV14_SKIP + ex de, hl ; DE = ciclos - 1 + pop hl ; direccion de retorno + ex (sp), hl ; HL = periodo (unidades Spectrum) — CALLEE + push de ; salva ciclos + push hl ; salva periodo original + ex de, hl ; DE = dividendo (periodo) + xor a ; A = resto + ld c, 14 + ld b, 16 +DIV14: + sla e ; desplaza el dividendo (bit 0 entra a 0; + rl d ; NO usar rl e: arrastraria el acarreo residual + rla ; del cp/sub de la vuelta anterior) + cp c + jr c, DIV14_SKIP + sub c + inc e +DIV14_SKIP: + djnz DIV14 ; DE = periodo / 14 + pop hl ; periodo original + or a + sbc hl, de ; HL = periodo - periodo/14 (= periodo*13/14) + dec hl + dec hl ; -2 (~ -30.125*(1-13/14), redondeado) + pop de ; ciclos - 1 + push ix ; el bucle usa IX; el compilador usa IX como frame + call __ZX81SD_BEEPER + pop ix + ret + ENDP + pop namespace +#line 26 "/zxbasic/src/lib/arch/zx81sd/runtime/io/sound/beep.asm" + push namespace core +BEEP: + PROC + LOCAL BEEP_I_OK, BEEP_OCTAVE, BEEP_ERROR, BEEP_SEMITONES + call __FPSTACK_PUSH ; duracion -> pila FP + pop hl ; direccion de retorno + pop af + pop de + pop bc ; tono (float) desde la pila de maquina + push hl ; CALLEE + call __FPSTACK_PUSH ; tono -> pila FP +; Igual que la ROM ($03F8): separa el tono en parte entera (mem-0) y + ; fraccionaria, y deja en la pila 1 + 0.05776226 * frac(tono). + rst 28h ;; FP-CALC dur, tono. + defb $31 ;;duplicate dur, tono, tono. + defb $27 ;;int dur, tono, int(tono). + defb $C0 ;;st-mem-0 (tono entero a mem-0) + defb $03 ;;subtract dur, frac(tono). + defb $34 ;;stk-data constante 0.05776226 + defb $EC ;;Exponent: $7C, Bytes: 4 + defb $6C, $98, $1F, $F5 + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $0F ;;addition dur, 1 + 0.0577*frac. + defb $38 ;;end-calc +; mem-0 contiene el tono entero en formato entero-pequeno: + ; 0, signo (0/FF), LSB, MSB, 0. Comprueba -128 <= tono <= 127. + ld hl, FP_MEM_AREA + ld a, (hl) ; el primer byte debe ser 0 (forma entera) + and a + jp nz, BEEP_ERROR + inc hl + ld c, (hl) ; C = byte de signo (0/FF) + inc hl + ld b, (hl) ; B = LSB (complemento a dos) + ld a, b + rla + sbc a, a ; A = 0/FF segun el bit 7 de B + cp c ; debe coincidir con el signo + jp nz, BEEP_ERROR + inc hl + cp (hl) ; y el MSB debe ser 0/FF igualmente + jp nz, BEEP_ERROR + ld a, b ; A = tono + 60 + add a, $3C + jp p, BEEP_I_OK ; si -60 <= tono, sigue + jp po, BEEP_ERROR ; fuera de rango por abajo +BEEP_I_OK: ; aqui -60 <= tono <= 127, A = tono+60 (0-187) + ld b, $FA ; B = -6 octavas bajo el DO central +BEEP_OCTAVE: + inc b ; octava siguiente + sub $0C ; 12 semitonos = 1 octava + jr nc, BEEP_OCTAVE + add a, $0C ; A = semitonos sobre DO (0-11) + push bc ; B = desplazamiento de octava (-5..10) + ; HL = BEEP_SEMITONES + A*5 (LOC-MEM de la ROM) + ld c, a + add a, a + add a, a + add a, c ; A = A*5 (max 55, sin acarreo) + ld c, a + ld b, 0 + ld hl, BEEP_SEMITONES + add hl, bc + ; STACK-NUM: apila el float de la tabla (frecuencia del semitono) + ld a, (hl) + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + call __FPSTACK_PUSH + rst 28h ;; FP-CALC dur, factor, freq. + defb $04 ;;multiply dur, freq ajustada a frac(tono). + defb $38 ;;end-calc (HL -> exponente del resultado) + pop af ; A = desplazamiento de octava + add a, (hl) ; freq *= 2^octava (suma al exponente) + ld (hl), a + rst 28h ;; FP-CALC dur, freq. + defb $C0 ;;st-mem-0 (frecuencia a mem-0) + defb $02 ;;delete dur. + defb $31 ;;duplicate dur, dur. + defb $38 ;;end-calc + ; comprueba 0 <= duracion <= 10 (como FIND-INT1 + CP 11 de la ROM) + call __FPSTACK_POP + bit 7, e ; negativa -> error + jp nz, BEEP_ERROR + call __FTOU32REG ; DEHL = int(duracion) + ld a, d + or e + or h + jp nz, BEEP_ERROR + ld a, l + cp $0B + jp nc, BEEP_ERROR +; Calcula los parametros del bucle del beeper: + ; ciclos = duracion * frecuencia + ; periodo = 406250 / frecuencia - 30.125 (406250 = 3.25MHz / 8) + rst 28h ;; FP-CALC dur. + defb $E0 ;;get-mem-0 dur, freq. + defb $04 ;;multiply ciclos. + defb $E0 ;;get-mem-0 ciclos, freq. + defb $34 ;;stk-data constante 406250 + defb $80 ;;Exponent: $93, Bytes: 3 + defb $43 + defb $46, $5D, $40 + defb $01 ;;exchange ciclos, 406250, freq. + defb $05 ;;division ciclos, 406250/freq. + defb $34 ;;stk-data constante 30.125 + defb $35 ;;Exponent: $85, Bytes: 1 + defb $71 + defb $03 ;;subtract ciclos, periodo. + defb $38 ;;end-calc + call __FPSTACK_POP + call __FTOU32REG ; HL = periodo + push hl + call __FPSTACK_POP + call __FTOU32REG ; HL = ciclos + ex de, hl ; DE = ciclos + pop hl ; HL = periodo + ld a, d + or e + ret z ; duracion 0: nada que hacer (evita 65536 ciclos) + dec de ; DE = ciclos - 1 + push ix ; el beeper usa IX (frame pointer del compilador) + call __ZX81SD_BEEPER + pop ix + ret +BEEP_ERROR: + ld a, ERROR_IntOutOfRange + jp __ERROR +; Tabla de semitonos de la ROM ($046E): frecuencias de la octava central. + ; Octavas arriba/abajo = multiplicar por 2^n (se suma n al exponente). +BEEP_SEMITONES: + defb $89, $02, $D0, $12, $86 ; 261.625565290 DO + defb $89, $0A, $97, $60, $75 ; 277.182631135 DO# + defb $89, $12, $D5, $17, $1F ; 293.664768100 RE + defb $89, $1B, $90, $41, $02 ; 311.126983881 RE# + defb $89, $24, $D0, $53, $CA ; 329.627557039 MI + defb $89, $2E, $9D, $36, $B1 ; 349.228231549 FA + defb $89, $38, $FF, $49, $3E ; 369.994422674 FA# + defb $89, $43, $FF, $6A, $73 ; 391.995436072 SOL + defb $89, $4F, $A7, $00, $54 ; 415.304697513 SOL# + defb $89, $5C, $00, $00, $00 ; 440.000000000 LA + defb $89, $69, $14, $F6, $24 ; 466.163761616 LA# + defb $89, $76, $F1, $10, $05 ; 493.883301378 SI + ENDP + pop namespace +#line 19 "arch/zx81sd/opt1_beep_var.bas" + END diff --git a/tests/functional/arch/zx81sd/opt1_beep_var.bas b/tests/functional/arch/zx81sd/opt1_beep_var.bas new file mode 100644 index 000000000..c66a92d2d --- /dev/null +++ b/tests/functional/arch/zx81sd/opt1_beep_var.bas @@ -0,0 +1,4 @@ +' Beep with constants + +DIM a as Float = 1 +BEEP a, 0.0 diff --git a/tests/functional/arch/zx81sd/opt2_epilogue.asm b/tests/functional/arch/zx81sd/opt2_epilogue.asm new file mode 100644 index 000000000..82ce6b444 --- /dev/null +++ b/tests/functional/arch/zx81sd/opt2_epilogue.asm @@ -0,0 +1,72 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/opt2_epilogue.bas b/tests/functional/arch/zx81sd/opt2_epilogue.bas new file mode 100644 index 000000000..4085cb2e0 --- /dev/null +++ b/tests/functional/arch/zx81sd/opt2_epilogue.bas @@ -0,0 +1,2 @@ +REM Test empty program for prologue and epilogue +REM for ZX Next using O2 optimizer diff --git a/tests/functional/arch/zx81sd/opt4_mul8.asm b/tests/functional/arch/zx81sd/opt4_mul8.asm new file mode 100644 index 000000000..4ace4c026 --- /dev/null +++ b/tests/functional/arch/zx81sd/opt4_mul8.asm @@ -0,0 +1,175 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_x: + DEFB 00, 00 +_y: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, 1 + ld (_y), a + ld h, 16 + call .core.__MUL8_FAST + push af + ld hl, (_x) + ld de, 16 + call .core.__MUL16_FAST + push hl + call _printTest + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_printTest: + push ix + ld ix, 0 + add ix, sp + ld a, (ix+7) + ld (0), a +_printTest__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret + ;; --- 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 34 "arch/zx81sd/opt4_mul8.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 35 "arch/zx81sd/opt4_mul8.bas" + END diff --git a/tests/functional/arch/zx81sd/opt4_mul8.bas b/tests/functional/arch/zx81sd/opt4_mul8.bas new file mode 100644 index 000000000..70095abc4 --- /dev/null +++ b/tests/functional/arch/zx81sd/opt4_mul8.bas @@ -0,0 +1,10 @@ +dim x as uinteger +dim y as ubyte + +y = 1 + +printTest(x*16,y*16) + +sub printTest(xpos as uinteger, ypos as ubyte) + poke 0, ypos +end sub diff --git a/tests/functional/arch/zx81sd/print_at2.asm b/tests/functional/arch/zx81sd/print_at2.asm new file mode 100644 index 000000000..8b848c84d --- /dev/null +++ b/tests/functional/arch/zx81sd/print_at2.asm @@ -0,0 +1,3310 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 +_row: + DEFB 00 +_col: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_row) + add a, 2 + push af + ld a, (_col) + inc a + call .core.PRINT_AT + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx81sd/print_at2.bas" + END diff --git a/tests/functional/arch/zx81sd/print_at2.bas b/tests/functional/arch/zx81sd/print_at2.bas new file mode 100644 index 000000000..587b55c35 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_at2.bas @@ -0,0 +1,2 @@ +DIM row, col as UByte +PRINT AT row + 2, col + 1; diff --git a/tests/functional/arch/zx81sd/print_f.asm b/tests/functional/arch/zx81sd/print_f.asm new file mode 100644 index 000000000..a0d85f416 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_f.asm @@ -0,0 +1,3808 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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__: + 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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 14 "arch/zx81sd/print_f.bas" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/printf.asm" + ; printf.asm (zx81sd) — PRINT de un numero FLOAT + ; + ; Sustituye a zx48k/runtime/printf.asm, que usa el literal 'str$' ($2Eh) del + ; calculador de la ROM (STR$ + STK-STO-$ + heap temporal) para despues + ; imprimir la cadena resultante. Aqui se usa directamente fp_tostr.asm + ; (conversion simplificada ya portada) e imprime sus caracteres uno a uno, + ; sin pasar por el heap. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_tostr.asm" + ; fp_tostr.asm (zx81sd) — Conversión de un FLOAT a texto ASCII decimal + ; + ; Usado por printf.asm (PRINT de un FLOAT) y str.asm (STR$) en zx81sd. + ; + ; La ROM del Spectrum resuelve esto con PRINT-FP, una rutina enorme que + ; además soporta notación científica (E-format) y se apoya en mecanismos + ; ajenos a zx81sd (canales CHAN-OPEN, área de "workspace" del editor BASIC + ; que crece con BC-SPACES). Aquí, por acuerdo explícito, se implementa una +; versión simplificada: signo + parte entera + hasta 5 decimales, sin + ; notación científica, recortando ceros finales (y el punto si todos los + ; decimales resultan cero). Cubre el uso habitual de PRINT/STR$ con FLOAT. + ; +; Se apoya en el calculador ya portado (fp_calc.asm: duplicate/int/subtract/ +; negate) y en rutinas Z80 puras ya existentes y verificadas en zx81sd: + ; __FTOU32REG/__FTOU8 (conversión FLOAT -> entero, sin ROM) y __DIVU32. +#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 + 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/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 20 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_tostr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 21 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_tostr.asm" + push namespace core + ; --- Variables de trabajo (no reentrante, uso transitorio) ------------------ +FP_STR_ORIG: defb 0, 0, 0, 0, 0 ; A E D C B del valor (se vuelve positivo) +FP_STR_INT: defb 0, 0, 0, 0, 0 ; parte entera +FP_STR_FRAC: defb 0, 0, 0, 0, 0 ; parte fraccionaria (se actualiza cada iteración) +FP_STR_COUNT: defb 0 ; contador de decimales restantes +FP_STR_WR: defw 0 ; cursor de escritura en FP_STR_BUF +FP_STR_BUF: defs 24 ; buffer de salida (signo + entero + '.' + decimales) + ; --------------------------------------------------------------------------- + ; STORE5 — Guarda A,E,D,C,B en (HL),(HL+1)..(HL+4) + ; LOAD5 — Carga A,E,D,C,B desde (HL),(HL+1)..(HL+4) + ; (Z80 no tiene LD E,(nn)/LD D,(nn)/etc, solo LD A,(nn) y LD rr,(nn) con pares; + ; por eso se accede siempre indirectamente vía HL.) + ; --------------------------------------------------------------------------- +STORE5: + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret +LOAD5: + ld a, (hl) + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ret + ; --------------------------------------------------------------------------- + ; EMIT_CHAR — Escribe A en el buffer de salida y avanza el cursor + ; --------------------------------------------------------------------------- +EMIT_CHAR: + push hl + ld hl, (FP_STR_WR) + ld (hl), a + inc hl + ld (FP_STR_WR), hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; EMIT_U32 — Escribe DEHL (entero sin signo de 32 bits) como digitos decimales + ; en el buffer de salida (sin ceros a la izquierda; "0" si el valor es cero). + ; Mismo algoritmo que __PRINTU32 (printi32.asm/printnum.asm), pero escribiendo + ; en el buffer en vez de en pantalla. + ; --------------------------------------------------------------------------- +EMIT_U32: + PROC + LOCAL EMIT_U32_LOOP + LOCAL EMIT_U32_START + LOCAL EMIT_U32_CONT + ld b, 0 +EMIT_U32_LOOP: + ld a, h + or l + or d + or e + jp z, EMIT_U32_START + push bc + ld bc, 0 + push bc + ld bc, 10 + push bc + call __DIVU32 + pop bc + exx + ld a, l + or '0' + push af + exx + inc b + jp EMIT_U32_LOOP +EMIT_U32_START: + ld a, b + or a + jp nz, EMIT_U32_CONT + ld a, '0' + call EMIT_CHAR + ret +EMIT_U32_CONT: + pop af + push bc + call EMIT_CHAR + pop bc + djnz EMIT_U32_CONT + ret + ENDP + ; --------------------------------------------------------------------------- + ; FP_TO_STR — Convierte un FLOAT a texto ASCII decimal +; Entrada: A,E,D,C,B = valor FLOAT (convención habitual del runtime) +; Salida: HL = puntero al texto (sin prefijo de longitud), BC = longitud + ; --------------------------------------------------------------------------- +FP_TO_STR: + PROC + LOCAL FP_TO_STR_POS + LOCAL FP_TO_STR_POS2 + LOCAL FP_TO_STR_FRACLOOP + LOCAL FP_TO_STR_FRACDONE + LOCAL FP_TO_STR_TRIMLOOP + LOCAL FP_TO_STR_TRIMDOT + LOCAL FP_TO_STR_TRIMKEEP + LOCAL FP_TO_STR_DONE + ld hl, FP_STR_ORIG + call STORE5 + ld hl, FP_STR_BUF + ld (FP_STR_WR), hl + ; ¿Es cero? (forma canonica: A=0 y mantisa completa a 0) + or e + or d + or c + or b + jr nz, FP_TO_STR_POS + ld a, '0' + call EMIT_CHAR + jp FP_TO_STR_DONE +FP_TO_STR_POS: + ; El bit 7 de E es el signo tanto en formato entero-pequeno como en + ; coma flotante completa (ver fp_calc.asm / __FTOU32REG). + ld a, (FP_STR_ORIG + 1) + bit 7, a + jr z, FP_TO_STR_POS2 + ld a, '-' + call EMIT_CHAR + ; Vuelve positivo el valor original (negate) para trabajar siempre en abs + ld hl, FP_STR_ORIG + call LOAD5 + call __FPSTACK_PUSH + rst 28h + defb $1B ;;negate + defb $38 ;;end-calc + call __FPSTACK_POP + ld hl, FP_STR_ORIG + call STORE5 +FP_TO_STR_POS2: + ; intx = INT(x) (x ya es >= 0 en este punto) + ld hl, FP_STR_ORIG + call LOAD5 + call __FPSTACK_PUSH + rst 28h + defb $31 ;;duplicate + defb $27 ;;int + defb $38 ;;end-calc + call __FPSTACK_POP + ld hl, FP_STR_INT + call STORE5 + ; frac = x - intx + ld hl, FP_STR_INT + call LOAD5 + call __FPSTACK_PUSH + rst 28h + defb $03 ;;subtract + defb $38 ;;end-calc + call __FPSTACK_POP + ld hl, FP_STR_FRAC + call STORE5 + ; imprime la parte entera (ya es >= 0, cabe en 32 bits sin signo) + ld hl, FP_STR_INT + call LOAD5 + call __FTOU32REG ; DEHL = parte entera + call EMIT_U32 + ld a, '.' + call EMIT_CHAR + ld a, 5 + ld (FP_STR_COUNT), a +FP_TO_STR_FRACLOOP: + ld hl, FP_STR_FRAC + call LOAD5 + call __FPSTACK_PUSH ; pila: [frac] + xor a + ld d, 10 + ld e, a + ld c, a + ld b, a + call __FPSTACK_PUSH ; pila: [frac, 10] + rst 28h + defb $04 ;;multiply + defb $38 ;;end-calc + ; pila: [frac*10] + rst 28h + defb $31 ;;duplicate + defb $27 ;;int + defb $38 ;;end-calc + ; pila: [frac*10, digito] + call __FPSTACK_POP + call __FTOU8 ; A = digito (0-9) + push af ; guarda el digito (igual que val.asm con rst 28h) + pop af + push af + ld d, a + xor a + ld e, a + ld c, a + ld b, a + call __FPSTACK_PUSH ; pila: [frac*10, digito] + rst 28h + defb $03 ;;subtract + defb $38 ;;end-calc + ; pila: [frac*10 - digito] = nuevo frac + call __FPSTACK_POP + ld hl, FP_STR_FRAC + call STORE5 + pop af ; recupera el digito + or '0' + call EMIT_CHAR + ld hl, FP_STR_COUNT + dec (hl) + jr nz, FP_TO_STR_FRACLOOP +FP_TO_STR_FRACDONE: + ; recorta ceros finales (y el punto si todos los decimales eran cero) + ld hl, (FP_STR_WR) +FP_TO_STR_TRIMLOOP: + dec hl + ld a, (hl) + cp '.' + jr z, FP_TO_STR_TRIMDOT + cp '0' + jr nz, FP_TO_STR_TRIMKEEP + jr FP_TO_STR_TRIMLOOP +FP_TO_STR_TRIMDOT: + ld (FP_STR_WR), hl + jp FP_TO_STR_DONE +FP_TO_STR_TRIMKEEP: + inc hl + ld (FP_STR_WR), hl +FP_TO_STR_DONE: + ld hl, (FP_STR_WR) + ld de, FP_STR_BUF + or a + sbc hl, de ; HL = longitud del texto + ld b, h + ld c, l + ld hl, FP_STR_BUF + ret + ENDP + pop namespace +#line 10 "/zxbasic/src/lib/arch/zx81sd/runtime/printf.asm" + push namespace core +__PRINTF: + ; Entrada: A,E,D,C,B = valor FLOAT + call FP_TO_STR ; HL = puntero al texto, BC = longitud + PROC + LOCAL __PRINTF_LOOP +__PRINTF_LOOP: + ld a, b + or c + ret z + ld a, (hl) + push hl + push bc + call __PRINTCHAR + pop bc + pop hl + inc hl + dec bc + jr __PRINTF_LOOP + ENDP + pop namespace +#line 15 "arch/zx81sd/print_f.bas" + END diff --git a/tests/functional/arch/zx81sd/print_f.bas b/tests/functional/arch/zx81sd/print_f.bas new file mode 100644 index 000000000..2a5d6a782 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_f.bas @@ -0,0 +1,2 @@ +DIM a as Float +PRINT a; diff --git a/tests/functional/arch/zx81sd/print_i16.asm b/tests/functional/arch/zx81sd/print_i16.asm new file mode 100644 index 000000000..5567769fb --- /dev/null +++ b/tests/functional/arch/zx81sd/print_i16.asm @@ -0,0 +1,3450 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 12 "arch/zx81sd/print_i16.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/printi16.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" + ; 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/zx48k/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/zx48k/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 13 "arch/zx81sd/print_i16.bas" + END diff --git a/tests/functional/arch/zx81sd/print_i16.bas b/tests/functional/arch/zx81sd/print_i16.bas new file mode 100644 index 000000000..62834fbab --- /dev/null +++ b/tests/functional/arch/zx81sd/print_i16.bas @@ -0,0 +1,2 @@ +DIM a as Integer +PRINT a; diff --git a/tests/functional/arch/zx81sd/print_i32.asm b/tests/functional/arch/zx81sd/print_i32.asm new file mode 100644 index 000000000..d9be6b54c --- /dev/null +++ b/tests/functional/arch/zx81sd/print_i32.asm @@ -0,0 +1,3504 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 13 "arch/zx81sd/print_i32.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/printi32.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 3 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 14 "arch/zx81sd/print_i32.bas" + END diff --git a/tests/functional/arch/zx81sd/print_i32.bas b/tests/functional/arch/zx81sd/print_i32.bas new file mode 100644 index 000000000..37dc14598 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_i32.bas @@ -0,0 +1,2 @@ +DIM a as Long +PRINT a; diff --git a/tests/functional/arch/zx81sd/print_i8.asm b/tests/functional/arch/zx81sd/print_i8.asm new file mode 100644 index 000000000..3672322d1 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_i8.asm @@ -0,0 +1,3424 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 12 "arch/zx81sd/print_i8.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 13 "arch/zx81sd/print_i8.bas" + END diff --git a/tests/functional/arch/zx81sd/print_i8.bas b/tests/functional/arch/zx81sd/print_i8.bas new file mode 100644 index 000000000..68878a0c4 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_i8.bas @@ -0,0 +1,2 @@ +DIM a as Byte +PRINT a; diff --git a/tests/functional/arch/zx81sd/print_tab2.asm b/tests/functional/arch/zx81sd/print_tab2.asm new file mode 100644 index 000000000..a34087230 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_tab2.asm @@ -0,0 +1,3305 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 +_i: + DEFB 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call .core.COPY_ATTR + ld a, (_i) + add a, 2 + call .core.PRINT_TAB + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 13 "arch/zx81sd/print_tab2.bas" + END diff --git a/tests/functional/arch/zx81sd/print_tab2.bas b/tests/functional/arch/zx81sd/print_tab2.bas new file mode 100644 index 000000000..02c7af670 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_tab2.bas @@ -0,0 +1,3 @@ +DIM i As UByte + +PRINT TAB i + 2; diff --git a/tests/functional/arch/zx81sd/print_u16.asm b/tests/functional/arch/zx81sd/print_u16.asm new file mode 100644 index 000000000..60094f9ed --- /dev/null +++ b/tests/functional/arch/zx81sd/print_u16.asm @@ -0,0 +1,3452 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 12 "arch/zx81sd/print_u16.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printu16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/printi16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/runtime/printi16.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" + ; 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/zx48k/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/zx48k/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/zx48k/runtime/printu16.asm" +#line 13 "arch/zx81sd/print_u16.bas" + END diff --git a/tests/functional/arch/zx81sd/print_u16.bas b/tests/functional/arch/zx81sd/print_u16.bas new file mode 100644 index 000000000..a98edf98d --- /dev/null +++ b/tests/functional/arch/zx81sd/print_u16.bas @@ -0,0 +1,2 @@ +DIM a as UInteger +PRINT a; diff --git a/tests/functional/arch/zx81sd/print_u32.asm b/tests/functional/arch/zx81sd/print_u32.asm new file mode 100644 index 000000000..2b164cfa6 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_u32.asm @@ -0,0 +1,3506 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 13 "arch/zx81sd/print_u32.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" + 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/zx48k/runtime/printi32.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 3 "/zxbasic/src/lib/arch/zx48k/runtime/printi32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/runtime/printu32.asm" +#line 14 "arch/zx81sd/print_u32.bas" + END diff --git a/tests/functional/arch/zx81sd/print_u32.bas b/tests/functional/arch/zx81sd/print_u32.bas new file mode 100644 index 000000000..57b6efbf2 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_u32.bas @@ -0,0 +1,2 @@ +DIM a as ULong +PRINT a; diff --git a/tests/functional/arch/zx81sd/print_u8.asm b/tests/functional/arch/zx81sd/print_u8.asm new file mode 100644 index 000000000..bb6fc246d --- /dev/null +++ b/tests/functional/arch/zx81sd/print_u8.asm @@ -0,0 +1,3426 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 12 "arch/zx81sd/print_u8.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/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/zx48k/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/runtime/printu8.asm" +#line 13 "arch/zx81sd/print_u8.bas" + END diff --git a/tests/functional/arch/zx81sd/print_u8.bas b/tests/functional/arch/zx81sd/print_u8.bas new file mode 100644 index 000000000..1f2bf2f99 --- /dev/null +++ b/tests/functional/arch/zx81sd/print_u8.bas @@ -0,0 +1,2 @@ +DIM a as Ubyte +PRINT a; diff --git a/tests/functional/arch/zx81sd/sgnf.asm b/tests/functional/arch/zx81sd/sgnf.asm new file mode 100644 index 000000000..0ffdbbc41 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnf.asm @@ -0,0 +1,121 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_y) + ld de, (_y + 1) + ld bc, (_y + 3) + call .core.__SGNF + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf.asm" +#line 14 "arch/zx81sd/sgnf.bas" + END diff --git a/tests/functional/arch/zx81sd/sgnf.bas b/tests/functional/arch/zx81sd/sgnf.bas new file mode 100644 index 000000000..e652173d7 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnf.bas @@ -0,0 +1,2 @@ +dim y as Float=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgnf16.asm b/tests/functional/arch/zx81sd/sgnf16.asm new file mode 100644 index 000000000..2e1c5f9c5 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnf16.asm @@ -0,0 +1,119 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 00h + DEFB 00h + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + ld de, (_y + 2) + call .core.__SGNF16 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgnf16.asm" +#line 13 "arch/zx81sd/sgnf16.bas" + END diff --git a/tests/functional/arch/zx81sd/sgnf16.bas b/tests/functional/arch/zx81sd/sgnf16.bas new file mode 100644 index 000000000..717a60512 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnf16.bas @@ -0,0 +1,2 @@ +dim y as Fixed=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgni16.asm b/tests/functional/arch/zx81sd/sgni16.asm new file mode 100644 index 000000000..b3f17ba42 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgni16.asm @@ -0,0 +1,116 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + call .core.__SGNI16 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgni16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgni16.asm" +#line 12 "arch/zx81sd/sgni16.bas" + END diff --git a/tests/functional/arch/zx81sd/sgni16.bas b/tests/functional/arch/zx81sd/sgni16.bas new file mode 100644 index 000000000..ae9e6710d --- /dev/null +++ b/tests/functional/arch/zx81sd/sgni16.bas @@ -0,0 +1,2 @@ +dim y as Integer=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgni32.asm b/tests/functional/arch/zx81sd/sgni32.asm new file mode 100644 index 000000000..f5966d2bf --- /dev/null +++ b/tests/functional/arch/zx81sd/sgni32.asm @@ -0,0 +1,119 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + ld de, (_y + 2) + call .core.__SGNI32 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgni32.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgn.asm" + ; Returns SGN (SIGN) for 32, 16 and 8 bits signed integers, Fixed and FLOAT + push namespace core + PROC + LOCAL __ENDSGN +__SGNF: + or b + or c + or d + or e + ret z + ld a, e + jr __ENDSGN +__SGNF16: +__SGNI32: + ld a, h + or l + or e + or d + ret z + ld a, d + jr __ENDSGN +__SGNI16: + ld a, h + or l + ret z + ld a, h +__ENDSGN: + or a + ld a, 1 + ret p + neg + ret + ENDP + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sgni32.asm" +#line 13 "arch/zx81sd/sgni32.bas" + END diff --git a/tests/functional/arch/zx81sd/sgni32.bas b/tests/functional/arch/zx81sd/sgni32.bas new file mode 100644 index 000000000..8ae2f3c43 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgni32.bas @@ -0,0 +1,2 @@ +dim y as Long=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgni8.asm b/tests/functional/arch/zx81sd/sgni8.asm new file mode 100644 index 000000000..b90154ee5 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgni8.asm @@ -0,0 +1,89 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_y) + call .core.__SGNI8 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgni8.asm" + ; Returns SGN (SIGN) for 8 bits signed integer + push namespace core +__SGNI8: + or a + ret z + ld a, 1 + ret p + neg + ret + pop namespace +#line 12 "arch/zx81sd/sgni8.bas" + END diff --git a/tests/functional/arch/zx81sd/sgni8.bas b/tests/functional/arch/zx81sd/sgni8.bas new file mode 100644 index 000000000..ec06c5224 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgni8.bas @@ -0,0 +1,2 @@ +dim y as Byte=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgnu16.asm b/tests/functional/arch/zx81sd/sgnu16.asm new file mode 100644 index 000000000..6b911e476 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnu16.asm @@ -0,0 +1,89 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 01h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + call .core.__SGNU16 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnu16.asm" + ; Returns SGN (SIGN) for 16 bits unsigned integer + push namespace core +__SGNU16: + ld a, h + or l + ret z + ld a, 1 + ret + pop namespace +#line 12 "arch/zx81sd/sgnu16.bas" + END diff --git a/tests/functional/arch/zx81sd/sgnu16.bas b/tests/functional/arch/zx81sd/sgnu16.bas new file mode 100644 index 000000000..af1acc29c --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnu16.bas @@ -0,0 +1,2 @@ +dim y as uInteger=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgnu32.asm b/tests/functional/arch/zx81sd/sgnu32.asm new file mode 100644 index 000000000..aae93f5cc --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnu32.asm @@ -0,0 +1,94 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 01h + DEFB 00h + DEFB 00h + DEFB 00h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, (_y) + ld de, (_y + 2) + call .core.__SGNU32 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnu32.asm" + ; Returns SGN (SIGN) for 32 bits unsigned integer + push namespace core +__SGNU32: + ld a, h + or l + or d + or e + ret z + ld a, 1 + ret + pop namespace +#line 13 "arch/zx81sd/sgnu32.bas" + END diff --git a/tests/functional/arch/zx81sd/sgnu32.bas b/tests/functional/arch/zx81sd/sgnu32.bas new file mode 100644 index 000000000..00bc09c8f --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnu32.bas @@ -0,0 +1,2 @@ +dim y as uLong=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/sgnu8.asm b/tests/functional/arch/zx81sd/sgnu8.asm new file mode 100644 index 000000000..82a2d8615 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnu8.asm @@ -0,0 +1,87 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +_y: + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_y) + call .core.__SGNU8 + ld (0), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sgnu8.asm" + ; Returns SGN (SIGN) for 8 bits unsigned integera + push namespace core +__SGNU8: + or a + ret z + ld a, 1 + ret + pop namespace +#line 12 "arch/zx81sd/sgnu8.bas" + END diff --git a/tests/functional/arch/zx81sd/sgnu8.bas b/tests/functional/arch/zx81sd/sgnu8.bas new file mode 100644 index 000000000..497869458 --- /dev/null +++ b/tests/functional/arch/zx81sd/sgnu8.bas @@ -0,0 +1,2 @@ +dim y as uByte=1 +poke 0, sgn y diff --git a/tests/functional/arch/zx81sd/shli16.asm b/tests/functional/arch/zx81sd/shli16.asm new file mode 100644 index 000000000..89e56ecd4 --- /dev/null +++ b/tests/functional/arch/zx81sd/shli16.asm @@ -0,0 +1,95 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + add hl, hl + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + add hl, hl + ld (_a), hl + pop hl + ld (_a), hl + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shli16.bas b/tests/functional/arch/zx81sd/shli16.bas new file mode 100644 index 000000000..4e6f14062 --- /dev/null +++ b/tests/functional/arch/zx81sd/shli16.bas @@ -0,0 +1,7 @@ +DIM a as Integer +DIM b as UByte + +a = a << b +a = a << 1 +a = a << 0 +a = 0 << b diff --git a/tests/functional/arch/zx81sd/shli32.asm b/tests/functional/arch/zx81sd/shli32.asm new file mode 100644 index 000000000..0d3b04a7a --- /dev/null +++ b/tests/functional/arch/zx81sd/shli32.asm @@ -0,0 +1,115 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + ld de, (_a + 2) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + call .core.__SHL32 + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + call .core.__SHL32 + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_a), hl + ld (_a + 2), de + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bitwise/shl32.asm" + push namespace core +__SHL32: ; Left Logical Shift 32 bits + sla l + rl h + rl e + rl d + ret + pop namespace +#line 38 "arch/zx81sd/shli32.bas" + END diff --git a/tests/functional/arch/zx81sd/shli32.bas b/tests/functional/arch/zx81sd/shli32.bas new file mode 100644 index 000000000..cd3dcc7b3 --- /dev/null +++ b/tests/functional/arch/zx81sd/shli32.bas @@ -0,0 +1,7 @@ +DIM a as Long +DIM b as UByte + +a = a << b +a = a << 1 +a = a << 0 +a = 0 << b diff --git a/tests/functional/arch/zx81sd/shli8.asm b/tests/functional/arch/zx81sd/shli8.asm new file mode 100644 index 000000000..809df7421 --- /dev/null +++ b/tests/functional/arch/zx81sd/shli8.asm @@ -0,0 +1,93 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, (_b) + ld hl, (_a - 1) + or a + ld b, a + ld a, h + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + add a, a + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), a + add a, a + ld (_a), a + ld (_a), a + ld a, (_b) + xor a + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shli8.bas b/tests/functional/arch/zx81sd/shli8.bas new file mode 100644 index 000000000..6679c2329 --- /dev/null +++ b/tests/functional/arch/zx81sd/shli8.bas @@ -0,0 +1,7 @@ +DIM a as Byte +DIM b as UByte + +a = a << b +a = a << 1 +a = a << 0 +a = 0 << b diff --git a/tests/functional/arch/zx81sd/shlu16.asm b/tests/functional/arch/zx81sd/shlu16.asm new file mode 100644 index 000000000..89e56ecd4 --- /dev/null +++ b/tests/functional/arch/zx81sd/shlu16.asm @@ -0,0 +1,95 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + add hl, hl + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + add hl, hl + ld (_a), hl + pop hl + ld (_a), hl + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shlu16.bas b/tests/functional/arch/zx81sd/shlu16.bas new file mode 100644 index 000000000..3fccb9089 --- /dev/null +++ b/tests/functional/arch/zx81sd/shlu16.bas @@ -0,0 +1,7 @@ +DIM a as UInteger +DIM b as UByte + +a = a << b +a = a << 1 +a = a << 0 +a = 0 << b diff --git a/tests/functional/arch/zx81sd/shlu32.asm b/tests/functional/arch/zx81sd/shlu32.asm new file mode 100644 index 000000000..f116212b4 --- /dev/null +++ b/tests/functional/arch/zx81sd/shlu32.asm @@ -0,0 +1,115 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + ld de, (_a + 2) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + call .core.__SHL32 + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + call .core.__SHL32 + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_a), hl + ld (_a + 2), de + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bitwise/shl32.asm" + push namespace core +__SHL32: ; Left Logical Shift 32 bits + sla l + rl h + rl e + rl d + ret + pop namespace +#line 38 "arch/zx81sd/shlu32.bas" + END diff --git a/tests/functional/arch/zx81sd/shlu32.bas b/tests/functional/arch/zx81sd/shlu32.bas new file mode 100644 index 000000000..3e39ce8a5 --- /dev/null +++ b/tests/functional/arch/zx81sd/shlu32.bas @@ -0,0 +1,7 @@ +DIM a as ULong +DIM b as UByte + +a = a << b +a = a << 1 +a = a << 0 +a = 0 << b diff --git a/tests/functional/arch/zx81sd/shlu8.asm b/tests/functional/arch/zx81sd/shlu8.asm new file mode 100644 index 000000000..809df7421 --- /dev/null +++ b/tests/functional/arch/zx81sd/shlu8.asm @@ -0,0 +1,93 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, (_b) + ld hl, (_a - 1) + or a + ld b, a + ld a, h + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + add a, a + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), a + add a, a + ld (_a), a + ld (_a), a + ld a, (_b) + xor a + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shlu8.bas b/tests/functional/arch/zx81sd/shlu8.bas new file mode 100644 index 000000000..f1ae30b1e --- /dev/null +++ b/tests/functional/arch/zx81sd/shlu8.bas @@ -0,0 +1,7 @@ +DIM a as UByte +DIM b as UByte + +a = a << b +a = a << 1 +a = a << 0 +a = 0 << b diff --git a/tests/functional/arch/zx81sd/shri16.asm b/tests/functional/arch/zx81sd/shri16.asm new file mode 100644 index 000000000..b3dc5e28e --- /dev/null +++ b/tests/functional/arch/zx81sd/shri16.asm @@ -0,0 +1,97 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + sra h + rr l + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + srl h + rr l + ld (_a), hl + pop hl + ld (_a), hl + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shri16.bas b/tests/functional/arch/zx81sd/shri16.bas new file mode 100644 index 000000000..0178617ba --- /dev/null +++ b/tests/functional/arch/zx81sd/shri16.bas @@ -0,0 +1,7 @@ +DIM a as Integer +DIM b as UByte + +a = a >> b +a = a >> 1 +a = a >> 0 +a = 0 >> b diff --git a/tests/functional/arch/zx81sd/shri32.asm b/tests/functional/arch/zx81sd/shri32.asm new file mode 100644 index 000000000..698ea4dad --- /dev/null +++ b/tests/functional/arch/zx81sd/shri32.asm @@ -0,0 +1,115 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + ld de, (_a + 2) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + call .core.__SHRA32 + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + call .core.__SHRA32 + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_a), hl + ld (_a + 2), de + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bitwise/shra32.asm" + push namespace core +__SHRA32: ; Right Arithmetical Shift 32 bits + sra d + rr e + rr h + rr l + ret + pop namespace +#line 38 "arch/zx81sd/shri32.bas" + END diff --git a/tests/functional/arch/zx81sd/shri32.bas b/tests/functional/arch/zx81sd/shri32.bas new file mode 100644 index 000000000..758477186 --- /dev/null +++ b/tests/functional/arch/zx81sd/shri32.bas @@ -0,0 +1,7 @@ +DIM a as Long +DIM b as UByte + +a = a >> b +a = a >> 1 +a = a >> 0 +a = 0 >> b diff --git a/tests/functional/arch/zx81sd/shri8.asm b/tests/functional/arch/zx81sd/shri8.asm new file mode 100644 index 000000000..ff12ea832 --- /dev/null +++ b/tests/functional/arch/zx81sd/shri8.asm @@ -0,0 +1,93 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, (_b) + ld hl, (_a - 1) + or a + ld b, a + ld a, h + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + sra a + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), a + sra a + ld (_a), a + ld (_a), a + ld a, (_b) + xor a + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shri8.bas b/tests/functional/arch/zx81sd/shri8.bas new file mode 100644 index 000000000..7684feef5 --- /dev/null +++ b/tests/functional/arch/zx81sd/shri8.bas @@ -0,0 +1,7 @@ +DIM a as Byte +DIM b as UByte + +a = a >> b +a = a >> 1 +a = a >> 0 +a = 0 >> b diff --git a/tests/functional/arch/zx81sd/shru16.asm b/tests/functional/arch/zx81sd/shru16.asm new file mode 100644 index 000000000..f8fd1eb14 --- /dev/null +++ b/tests/functional/arch/zx81sd/shru16.asm @@ -0,0 +1,97 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + srl h + rr l + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + srl h + rr l + ld (_a), hl + pop hl + ld (_a), hl + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld (_a), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shru16.bas b/tests/functional/arch/zx81sd/shru16.bas new file mode 100644 index 000000000..1915574dd --- /dev/null +++ b/tests/functional/arch/zx81sd/shru16.bas @@ -0,0 +1,7 @@ +DIM a as UInteger +DIM b as UByte + +a = a >> b +a = a >> 1 +a = a >> 0 +a = 0 >> b diff --git a/tests/functional/arch/zx81sd/shru32.asm b/tests/functional/arch/zx81sd/shru32.asm new file mode 100644 index 000000000..b3f5f4570 --- /dev/null +++ b/tests/functional/arch/zx81sd/shru32.asm @@ -0,0 +1,115 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld a, (_b) + ld b, a + ld hl, (_a) + ld de, (_a + 2) + or a + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + call .core.__SHRL32 + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + call .core.__SHRL32 + ld (_a), hl + ld (_a + 2), de + ld hl, (_a) + ld de, (_a + 2) + ld (_a), hl + ld (_a + 2), de + ld a, (_b) + xor a + ld l, a + ld h, 0 + ld e, h + ld d, h + ld (_a), hl + ld (_a + 2), de + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bitwise/shrl32.asm" + push namespace core +__SHRL32: ; Right Logical Shift 32 bits + srl d + rr e + rr h + rr l + ret + pop namespace +#line 38 "arch/zx81sd/shru32.bas" + END diff --git a/tests/functional/arch/zx81sd/shru32.bas b/tests/functional/arch/zx81sd/shru32.bas new file mode 100644 index 000000000..34191e0ca --- /dev/null +++ b/tests/functional/arch/zx81sd/shru32.bas @@ -0,0 +1,7 @@ +DIM a as ULong +DIM b as UByte + +a = a >> b +a = a >> 1 +a = a >> 0 +a = 0 >> b diff --git a/tests/functional/arch/zx81sd/shru8.asm b/tests/functional/arch/zx81sd/shru8.asm new file mode 100644 index 000000000..7a92f2fce --- /dev/null +++ b/tests/functional/arch/zx81sd/shru8.asm @@ -0,0 +1,93 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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, (_b) + ld hl, (_a - 1) + or a + ld b, a + ld a, h + jr z, .LABEL.__LABEL1 +.LABEL.__LABEL0: + srl a + djnz .LABEL.__LABEL0 +.LABEL.__LABEL1: + ld (_a), a + srl a + ld (_a), a + ld (_a), a + ld a, (_b) + xor a + ld (_a), a + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/shru8.bas b/tests/functional/arch/zx81sd/shru8.bas new file mode 100644 index 000000000..c3a981afe --- /dev/null +++ b/tests/functional/arch/zx81sd/shru8.bas @@ -0,0 +1,7 @@ +DIM a as UByte +DIM b as UByte + +a = a >> b +a = a >> 1 +a = a >> 0 +a = 0 >> b diff --git a/tests/functional/arch/zx81sd/stdlib_SP_Fill.bas b/tests/functional/arch/zx81sd/stdlib_SP_Fill.bas new file mode 100644 index 000000000..acc51cb3d --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_SP_Fill.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_alloc.asm b/tests/functional/arch/zx81sd/stdlib_alloc.asm new file mode 100644 index 000000000..559d89341 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_alloc.asm @@ -0,0 +1,3035 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_allocate: +#line 36 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + push namespace core + ld b, h + ld c, l + jp __MEM_ALLOC + pop namespace +#line 43 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +_allocate__leave: + ret +_callocate: +#line 66 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + push namespace core + ld b, h + ld c, l + jp __MEM_CALLOC + pop namespace +#line 73 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +_callocate__leave: + ret +_deallocate: +#line 86 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + push namespace core + jp __MEM_FREE + pop namespace +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +_deallocate__leave: + ret +_reallocate: +#line 114 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + push namespace core + ex de, hl + pop hl + ex (sp), hl + ld b, h + ld c, l + ex de, hl + jp __REALLOC + pop namespace +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +_reallocate__leave: + ret +_memavail: +#line 135 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + push namespace core + PROC + LOCAL LOOP + ld hl, ZXBASIC_MEM_HEAP + ld de, 0 +LOOP: + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ex de, hl + add hl, bc + ex de, hl + ld a, h + or l + jr nz, LOOP + ex de, hl + ENDP + pop namespace +#line 172 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +_memavail__leave: + ret +_maxavail: +#line 181 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + push namespace core + PROC + LOCAL LOOP, CONT + ld hl, ZXBASIC_MEM_HEAP + ld de, 0 +LOOP: + ld c, (hl) + inc hl + ld b, (hl) + inc hl + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ex de, hl + or a + sbc hl, bc + add hl, bc + ex de, hl + jr nc, CONT + ld d, b + ld e, c +CONT: + ld a, h + or l + jr nz, LOOP + ex de, hl + ENDP + pop namespace +#line 228 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +_maxavail__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 233 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 234 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 235 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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. + ; --------------------------------------------------------------------- + ; 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 236 "/zxbasic/src/lib/arch/zx48k/stdlib/alloc.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_alloc.bas b/tests/functional/arch/zx81sd/stdlib_alloc.bas new file mode 100644 index 000000000..3c7a20be1 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_alloc.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_attr.asm b/tests/functional/arch/zx81sd/stdlib_attr.asm new file mode 100644 index 000000000..432153635 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_attr.asm @@ -0,0 +1,2505 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_attr: + push ix + ld ix, 0 + add ix, sp +#line 28 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + push namespace core + PROC + LOCAL __ATTR_END + ld e, (ix+7) + ld d, (ix+5) + call __IN_SCREEN + jr nc, __ATTR_END + call __ATTR_ADDR + ld a, (hl) +__ATTR_END: + ENDP + pop namespace +#line 49 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +_attr__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_setattr: + push ix + ld ix, 0 + add ix, sp +#line 66 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + push namespace core + PROC + LOCAL __ATTR_END + ld e, (ix+7) + ld d, (ix+5) + call __IN_SCREEN + jr nc, __ATTR_END + call __ATTR_ADDR + ld a, (ix+9) + ld (hl), a +__ATTR_END: + ENDP + pop namespace +#line 88 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +_setattr__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +_attraddr: +#line 102 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + push namespace core + pop hl + ex (sp), hl + ld d, a + ld e, h + jp __ATTR_ADDR + pop namespace +#line 112 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" +_attraddr__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/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 5 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 117 "/zxbasic/src/lib/arch/zx48k/stdlib/attr.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_attr.bas b/tests/functional/arch/zx81sd/stdlib_attr.bas new file mode 100644 index 000000000..e7fe7d2cf --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_attr.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_basic.asm b/tests/functional/arch/zx81sd/stdlib_basic.asm new file mode 100644 index 000000000..ffe074176 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_basic.asm @@ -0,0 +1,2415 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_EvalBASIC: +#line 49 "/zxbasic/src/lib/arch/zx48k/stdlib/basic.bas" + push namespace core + PROC + LOCAL E_LINE + LOCAL CH_ADD + LOCAL SET_MIN + LOCAL MAKE_ROOM + LOCAL K_CUR + LOCAL LINE_SCAN + LOCAL LINE_RUN + LOCAL DEF_ADD + LOCAL NEWPPC + LOCAL NSPPC + LOCAL PPC + LOCAL SUBPPC + LOCAL NXTLIN + E_LINE equ 5c59h + CH_ADD equ 5c5dh + SET_MIN equ 16b0h + MAKE_ROOM equ 1655h + K_CUR equ 5c5bh + LINE_SCAN equ 1b17h + LINE_RUN equ 1b8ah + DEF_ADD equ 5c0bh + NEWPPC equ 5c42h + NSPPC equ 5c44h + PPC equ 5c45h + SUBPPC equ 5c47h + NXTLIN equ 5c55h + ld a, h + or l + ret z + ld de,(CH_ADD) + push de + ld de,(NXTLIN) + push de + ld de,(PPC) + push de + ld a,(SUBPPC) + push af + ld de,(NEWPPC) + push de + ld a,(NSPPC) + push af + push ix + ld c, (hl) + inc hl + ld b, (hl) + inc hl + push hl + push bc + call SET_MIN + ld hl,(E_LINE) + pop bc + push bc + call MAKE_ROOM + pop bc + pop hl + ld de,(E_LINE) + ldir + ld (K_CUR),de + call LINE_SCAN + bit 7,(iy+0) + ld a, ERROR_NonsenseInBasic + jp z, __ERROR + ld hl,(E_LINE) + ld (CH_ADD),hl + set 7,(iy+1) + ld (iy+0),0xff + ld (iy+10),1 + call LINE_RUN + pop ix + pop af + ld (NSPPC),a + pop hl + ld (NEWPPC),hl + pop af + ld (SUBPPC),a + pop hl + ld (PPC),hl + pop hl + ld (NXTLIN),hl + pop hl + ld (CH_ADD),hl + ENDP + pop namespace +#line 143 "/zxbasic/src/lib/arch/zx48k/stdlib/basic.bas" +_EvalBASIC__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 148 "/zxbasic/src/lib/arch/zx48k/stdlib/basic.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_basic.bas b/tests/functional/arch/zx81sd/stdlib_basic.bas new file mode 100644 index 000000000..306234257 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_basic.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_clearbox.asm b/tests/functional/arch/zx81sd/stdlib_clearbox.asm new file mode 100644 index 000000000..dc382bf38 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_clearbox.asm @@ -0,0 +1,2390 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_clearBox: + push ix + ld ix, 0 + add ix, sp +#line 37 "/zxbasic/src/lib/arch/zx48k/stdlib/clearbox.bas" + PROC + LOCAL clearbox_outer_loop, clearbox_mid_loop + LOCAL clearbox_inner_loop, clearbox_row_skip + ld b,(IX+5) + ld c,(IX+7) + ld a, c + and 24 + ld h, a + ld a, c + and 7 + rra + rra + rra + rra + add a, b + ld l, a + ld b, (IX+11) + ld c,(IX+9) +clearbox_outer_loop: + xor a + push bc + push hl + ld de, (.core.SCREEN_ADDR) + add hl, de + ld d, 8 +clearbox_mid_loop: + push hl + ld b,c +clearbox_inner_loop: + ld (hl), a + inc hl + djnz clearbox_inner_loop + pop hl + inc h + dec d + jp nz, clearbox_mid_loop + pop hl + pop bc + ld a, 32 + add a, l + ld l, a + jr nc, clearbox_row_skip + ld a, 8 + add a, h + ld h, a +clearbox_row_skip: + djnz clearbox_outer_loop + ENDP +#line 101 "/zxbasic/src/lib/arch/zx48k/stdlib/clearbox.bas" +_clearBox__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 115 "/zxbasic/src/lib/arch/zx48k/stdlib/clearbox.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_clearbox.bas b/tests/functional/arch/zx81sd/stdlib_clearbox.bas new file mode 100644 index 000000000..a9303731c --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_clearbox.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_csrlin.asm b/tests/functional/arch/zx81sd/stdlib_csrlin.asm new file mode 100644 index 000000000..d500cca1d --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_csrlin.asm @@ -0,0 +1,2448 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_csrlin: +#line 26 "/zxbasic/src/lib/arch/zx48k/stdlib/csrlin.bas" + push namespace core + PROC + call __LOAD_S_POSN + ld a, d + ENDP + pop namespace +#line 36 "/zxbasic/src/lib/arch/zx48k/stdlib/csrlin.bas" +_csrlin__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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 41 "/zxbasic/src/lib/arch/zx48k/stdlib/csrlin.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_csrlin.bas b/tests/functional/arch/zx81sd/stdlib_csrlin.bas new file mode 100644 index 000000000..135e44c0f --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_csrlin.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_hex.asm b/tests/functional/arch/zx81sd/stdlib_hex.asm new file mode 100644 index 000000000..7f30ec65c --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_hex.asm @@ -0,0 +1,3040 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_hex: +#line 30 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" + push namespace core + PROC + LOCAL SUB_CHAR + LOCAL SUB_CHAR2 + LOCAL END_CHAR + LOCAL DIGIT + push hl + push de + ld bc,10 + call __MEM_ALLOC + ld a, h + or l + pop de + pop bc + ret z + push hl + ld (hl), 8 + inc hl + ld (hl), 0 + inc hl + call DIGIT + ld d, e + call DIGIT + ld d, b + call DIGIT + ld d, c + call DIGIT + pop hl + ret +DIGIT: + ld a, d + call SUB_CHAR + ld a, d + jr SUB_CHAR2 +SUB_CHAR: + rrca + rrca + rrca + rrca +SUB_CHAR2: + and 0Fh + add a, '0' + cp '9' + 1 + jr c, END_CHAR + add a, 7 +END_CHAR: + ld (hl), a + inc hl + ret + ENDP + pop namespace +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +_hex__leave: + ret +_hex16: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix+4) + ld h, (ix+5) + ld de, 0 + call _hex + ld d, h + ld e, l + ld bc, -2 + call .core.__PSTORE_STR2 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld hl, 4 + push hl + ld hl, 7 + push hl + xor a + call .core.__STRSLICE +_hex16__leave: + ex af, af' + exx + ld l, (ix-2) + ld h, (ix-1) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_hex8: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld a, (ix+5) + ld l, a + ld h, 0 + ld e, h + ld d, h + call _hex + ld d, h + ld e, l + ld bc, -2 + call .core.__PSTORE_STR2 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld hl, 6 + push hl + ld hl, 7 + push hl + xor a + call .core.__STRSLICE +_hex8__leave: + ex af, af' + exx + ld l, (ix-2) + ld h, (ix-1) + 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/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 174 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 175 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" +; vim:ts=4:et:sw=4 + ; + ; Stores an string (pointer to the HEAP by DE) into the address pointed + ; by (IX + BC). No new copy of the string is created into the HEAP, since + ; it's supposed it's already created (temporary string) + ; +#line 1 "/zxbasic/src/lib/arch/zx48k/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. + 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 9 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" + push namespace core +__PSTORE_STR2: + push ix + pop hl + add hl, bc + jp __STORE_STR2 + pop namespace +#line 176 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strslice.asm" + ; String slicing library + ; HL = Str pointer + ; DE = String start + ; BC = String character end + ; A register => 0 => the HL pointer wont' be freed from the HEAP + ; e.g. a$(5 TO 10) => HL = a$; DE = 5; BC = 10 + ; This implements a$(X to Y) being X and Y first and + ; last characters respectively. If X > Y, NULL is returned + ; Otherwise returns a pointer to a$ FROM X to Y (starting from 0) + ; if Y > len(a$), then a$ will be padded with spaces (reallocating + ; it in dynamic memory if needed). Returns pointer (HL) to resulting + ; string. NULL (0) if no memory for padding. + ; +#line 1 "/zxbasic/src/lib/arch/zx48k/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 18 "/zxbasic/src/lib/arch/zx48k/runtime/strslice.asm" + push namespace core +__STRSLICE: ; Callee entry + pop hl ; Return ADDRESS + pop bc ; Last char pos + pop de ; 1st char pos + ex (sp), hl ; CALLEE. -> String start +__STRSLICE_FAST: ; __FASTCALL__ Entry + PROC + LOCAL __CONT + LOCAL __EMPTY + LOCAL __FREE_ON_EXIT + push hl ; Stores original HL pointer to be recovered on exit + ex af, af' ; Saves A register for later + push hl + call __STRLEN + inc bc ; Last character position + 1 (string starts from 0) + or a + sbc hl, bc ; Compares length with last char position + jr nc, __CONT ; If Carry => We must copy to end of string + add hl, bc ; Restore back original LEN(a$) in HL + ld b, h + ld c, l ; Copy to the end of str + ccf ; Clears Carry flag for next subtraction +__CONT: + ld h, b + ld l, c ; HL = Last char position to copy (1 for char 0, 2 for char 1, etc) + sbc hl, de ; HL = LEN(a$) - DE => Number of chars to copy + jr z, __EMPTY ; 0 Chars to copy => Return HL = 0 (NULL STR) + jr c, __EMPTY ; If Carry => Nothing to return (NULL STR) + ld b, h + ld c, l ; BC = Number of chars to copy + inc bc + inc bc ; +2 bytes for string length number + push bc + push de + call __MEM_ALLOC + pop de + pop bc + ld a, h + or l + jr z, __EMPTY ; Return if NULL (no memory) + dec bc + dec bc ; Number of chars to copy (Len of slice) + ld (hl), c + inc hl + ld (hl), b + inc hl ; Stores new string length + ex (sp), hl ; Pointer to A$ now in HL; Pointer to new string chars in Stack + inc hl + inc hl ; Skip string length + add hl, de ; Were to start from A$ + pop de ; Start of new string chars + push de ; Stores it again + ldir ; Copies BC chars + pop de + dec de + dec de ; Points to String LEN start + ex de, hl ; Returns it in HL + jr __FREE_ON_EXIT +__EMPTY: ; Return NULL (empty) string + pop hl + ld hl, 0 ; Return NULL +__FREE_ON_EXIT: + ex af, af' ; Recover original A register + ex (sp), hl ; Original HL pointer + or a + call nz, __MEM_FREE + pop hl ; Recover result + ret + ENDP + pop namespace +#line 177 "/zxbasic/src/lib/arch/zx48k/stdlib/hex.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_hex.bas b/tests/functional/arch/zx81sd/stdlib_hex.bas new file mode 100644 index 000000000..605e8a9f0 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_hex.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_memcopy.asm b/tests/functional/arch/zx81sd/stdlib_memcopy.asm new file mode 100644 index 000000000..cd340564e --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_memcopy.asm @@ -0,0 +1,166 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_MemMove: +#line 31 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" + push namespace core + exx + pop hl + exx + pop de + pop bc + exx + push hl + exx + jp __MEMCPY + pop namespace +#line 51 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" +_MemMove__leave: + ret +_MemCopy: +#line 67 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" + push namespace core + exx + pop hl + exx + pop de + pop bc + exx + push hl + exx + ldir + pop namespace +#line 87 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" +_MemCopy__leave: + ret +_MemSet: +#line 100 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" + push namespace core + pop de + pop af + pop bc + push de + ld (hl),a + dec bc + ld a, b + or c + ret z + ld d,h + ld e,l + inc de + ldir + pop namespace +#line 122 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" +_MemSet__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/memcopy.asm" + ; ---------------------------------------------------------------- + ; This file is released under the MIT License + ; + ; Copyleft (k) 2008 +; by Jose Rodriguez-Rosa (a.k.a. Boriel) + ; + ; Use this file as a template to develop your own library file + ; ---------------------------------------------------------------- + ; Emulates both memmove and memcpy C routines + ; Block will be safely copied if they overlap + ; HL => Start of source block + ; DE => Start of destiny block + ; BC => Block length + push namespace core +__MEMCPY: + PROC + LOCAL __MEMCPY2 + push hl + add hl, bc ; addr of last source block byte + 1 + or a + sbc hl, de ; checks if DE > HL + BC + pop hl ; recovers HL. If carry => DE > HL + BC (no overlap) + jr c, __MEMCPY2 + ; Now checks if DE <= HL + sbc hl, de ; Even if overlap, if DE < HL then we can LDIR safely + add hl, de + jr nc, __MEMCPY2 + dec bc + add hl, bc + ex de, hl + add hl, bc + ex de, hl + inc bc ; HL and DE point to the last byte position + lddr ; Copies from end to beginning + ret +__MEMCPY2: + ldir + ret + ENDP + pop namespace +#line 127 "/zxbasic/src/lib/arch/zx48k/stdlib/memcopy.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_memcopy.bas b/tests/functional/arch/zx81sd/stdlib_memcopy.bas new file mode 100644 index 000000000..2b2b39bbc --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_memcopy.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_pos.asm b/tests/functional/arch/zx81sd/stdlib_pos.asm new file mode 100644 index 000000000..490dc8e76 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_pos.asm @@ -0,0 +1,2448 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_pos: +#line 28 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + push namespace core + PROC + call __LOAD_S_POSN + ld a, e + ENDP + pop namespace +#line 38 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" +_pos__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/lib/arch/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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 43 "/zxbasic/src/lib/arch/zx48k/stdlib/pos.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_pos.bas b/tests/functional/arch/zx81sd/stdlib_pos.bas new file mode 100644 index 000000000..d2ab75b0f --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_pos.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_putchars.asm b/tests/functional/arch/zx81sd/stdlib_putchars.asm new file mode 100644 index 000000000..a289d9b9e --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_putchars.asm @@ -0,0 +1,2861 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_putChars: + push ix + ld ix, 0 + add ix, sp +#line 34 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + PROC + LOCAL BLPutChar, BLPutCharColumnLoop, BLPutCharInColumnLoop, BLPutCharSameThird + LOCAL BLPutCharNextThird, BLPutCharNextColumn, BLPutCharsEnd +BLPutChar: + ld a,(ix+5) + ld l,a + ld a,(ix+7) + ld d,a + and 24 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + ld l,a + push hl + ld e,(ix+12) + ld d,(ix+13) + ld b,(ix+9) + push bc +BLPutCharColumnLoop: + ld b, (ix+11) +BLPutCharInColumnLoop: + push hl + push de + ld de, (.core.SCREEN_ADDR) + add hl, de + pop de + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + inc de + inc h + ld a,(de) + ld (hl),a + pop hl + inc de + dec b + jr z, BLPutCharNextColumn + push de + ld a,l + and 224 + cp 224 + jr z,BLPutCharNextThird +BLPutCharSameThird: + ld de,32 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextThird: + ld de,1824 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextColumn: + pop bc + pop hl + dec b + jr z, BLPutCharsEnd + inc l + push hl + push bc + jp BLPutCharColumnLoop +BLPutCharsEnd: + ENDP +#line 148 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" +_putChars__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_getChars: + push ix + ld ix, 0 + add ix, sp +#line 167 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + PROC + LOCAL BLGetChar, BLGetCharColumnLoop, BLGetCharInColumnLoop, BLGetCharSameThird + LOCAL BLGetCharNextThird, BLGetCharNextColumn, BLGetCharsEnd +BLGetChar: + ld a,(ix+5) + ld l,a + ld a,(ix+7) + ld d,a + and 24 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + ld l,a + push hl + ld e,(ix+12) + ld d,(ix+13) + ld b,(ix+9) + push bc +BLGetCharColumnLoop: + ld b, (ix+11) +BLGetCharInColumnLoop: + push hl + push de + ld de, (.core.SCREEN_ADDR) + add hl, de + pop de + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + inc de + inc h + ld a,(hl) + ld (de),a + pop hl + inc de + dec b + jr z, BLGetCharNextColumn + push de + ld a,l + and 224 + cp 224 + jr z,BLGetCharNextThird +BLGetCharSameThird: + ld de,32 + add hl,de + pop de + jp BLGetCharInColumnLoop +BLGetCharNextThird: + ld de,1824 + add hl,de + pop de + jp BLGetCharInColumnLoop +BLGetCharNextColumn: + pop bc + pop hl + dec b + jr z, BLGetCharsEnd + inc l + push hl + push bc + jp BLGetCharColumnLoop +BLGetCharsEnd: + ENDP +#line 281 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" +_getChars__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_paint: + push ix + ld ix, 0 + add ix, sp +#line 300 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + PROC + LOCAL BLPaintHeightLoop, BLPaintWidthLoop, BLPaintWidthExitLoop, BLPaintHeightExitLoop + ld a,(ix+7) + rrca + rrca + rrca + ld l,a + and 3 + ld h,a + ld a,l + and 224 + ld l,a + ld a,(ix+5) + add a,l + ld l,a + ld de,(.core.SCREEN_ATTR_ADDR) + add hl, de + push hl + ld a, (ix+13) + ld de,32 + ld c,(ix+11) +BLPaintHeightLoop: + ld b,(ix+9) +BLPaintWidthLoop: + ld (hl),a + inc hl + djnz BLPaintWidthLoop +BLPaintWidthExitLoop: + pop hl + dec c + jr z, BLPaintHeightExitLoop + add hl,de + push hl + jp BLPaintHeightLoop +BLPaintHeightExitLoop: + ENDP +#line 345 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" +_paint__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_paintData: + push ix + ld ix, 0 + add ix, sp +#line 365 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + PROC + LOCAL BLPaintDataHeightLoop, BLPaintDataWidthLoop, BLPaintDataWidthExitLoop, BLPaintDataHeightExitLoop + ld a,(ix+7) + rrca + rrca + rrca + ld l,a + and 3 + ld h,a + ld a,l + and 224 + ld l,a + ld a,(ix+5) + add a,l + ld l,a + ld de,(.core.SCREEN_ATTR_ADDR) + add hl, de + push hl + ld d,(ix+13) + ld e,(ix+12) + ld c,(ix+11) +BLPaintDataHeightLoop: + ld b,(ix+9) +BLPaintDataWidthLoop: + ld a,(de) + ld (hl),a + inc hl + inc de + djnz BLPaintDataWidthLoop +BLPaintDataWidthExitLoop: + pop hl + dec c + jr z, BLPaintDataHeightExitLoop + push de + ld de,32 + add hl,de + pop de + push hl + jp BLPaintDataHeightLoop +BLPaintDataHeightExitLoop: + ENDP +#line 414 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" +_paintData__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_getPaintData: + push ix + ld ix, 0 + add ix, sp +#line 433 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + PROC + LOCAL BLGetPaintDataHeightLoop, BLGetPaintDataWidthLoop, BLGetPaintDataWidthExitLoop, BLGetPaintDataHeightExitLoop + ld a,(ix+7) + rrca + rrca + rrca + ld l,a + and 3 + ld h,a + ld a,l + and 224 + ld l,a + ld a,(ix+5) + add a,l + ld l,a + ld de,(.core.SCREEN_ATTR_ADDR) + add hl, de + push hl + ld d,(ix+13) + ld e,(ix+12) + ld c,(ix+11) +BLGetPaintDataHeightLoop: + ld b,(ix+9) +BLGetPaintDataWidthLoop: + ld a,(hl) + ld (de),a + inc hl + inc de + djnz BLGetPaintDataWidthLoop +BLGetPaintDataWidthExitLoop: + pop hl + dec c + jr z, BLGetPaintDataHeightExitLoop + push de + ld de,32 + add hl,de + pop de + push hl + jp BLGetPaintDataHeightLoop +BLGetPaintDataHeightExitLoop: + ENDP +#line 482 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" +_getPaintData__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_putCharsOverMode: + push ix + ld ix, 0 + add ix, sp +#line 507 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + PROC + LOCAL BLPutChar, BLPutCharColumnLoop, BLPutCharInColumnLoop, BLPutCharSameThird + LOCAL BLPutCharNextThird, BLPutCharNextColumn, BLPutCharsEnd + LOCAL op1, op2, op3, op4, op5, op6, op7, op8, opTable, noCarry + ld a,(ix+13) + and 3 + ld hl, opTable + add a, l + jp nc, noCarry + inc h +noCarry: + ld l, a + ld a, (hl) + ld (op1), a + ld (op2), a + ld (op3), a + ld (op4), a + ld (op5), a + ld (op6), a + ld (op7), a + ld (op8), a + jp BLPutChar +opTable: + DEFB $00 + DEFB $AE + DEFB $A6 + DEFB $B6 +BLPutChar: + ld a,(ix+5) + ld l,a + ld a,(ix+7) + ld d,a + and 24 + ld h,a + ld a,d + and 7 + rrca + rrca + rrca + or l + ld l,a + push hl + ld e,(ix+14) + ld d,(ix+15) + ld b,(ix+9) + push bc +BLPutCharColumnLoop: + ld b, (ix+11) +BLPutCharInColumnLoop: + push hl + push de + ld de, (.core.SCREEN_ADDR) + add hl, de + pop de + ld a,(de) +op1: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op2: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op3: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op4: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op5: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op6: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op7: + nop + ld (hl),a + inc de + inc h + ld a,(de) +op8: + nop + ld (hl),a + pop hl + inc de + dec b + jr z, BLPutCharNextColumn + push de + ld a,l + and 224 + cp 224 + jr z,BLPutCharNextThird +BLPutCharSameThird: + ld de,32 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextThird: + ld de,1824 + add hl,de + pop de + jp BLPutCharInColumnLoop +BLPutCharNextColumn: + pop bc + pop hl + dec b + jr z, BLPutCharsEnd + inc l + push hl + push bc + jp BLPutCharColumnLoop +BLPutCharsEnd: + ENDP +#line 661 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" +_putCharsOverMode__leave: + exx + ld hl, 12 +__EXIT_FUNCTION: + ld sp, ix + pop ix + pop de + add hl, sp + ld sp, hl + push de + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 676 "/zxbasic/src/lib/arch/zx48k/stdlib/putchars.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_putchars.bas b/tests/functional/arch/zx81sd/stdlib_putchars.bas new file mode 100644 index 000000000..30371da29 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_putchars.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_scrbuffer.asm b/tests/functional/arch/zx81sd/stdlib_scrbuffer.asm new file mode 100644 index 000000000..de075cf67 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_scrbuffer.asm @@ -0,0 +1,2349 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_GetScreenBufferAddr: +#line 26 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" + ld hl, (.core.SCREEN_ADDR) +#line 29 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" +_GetScreenBufferAddr__leave: + ret +_SetScreenBufferAddr: +#line 44 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" + ld (.core.SCREEN_ADDR), hl +#line 47 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" +_SetScreenBufferAddr__leave: + ret +_GetAttrBufferAddr: +#line 57 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" + ld hl, (.core.SCREEN_ATTR_ADDR) +#line 60 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" +_GetAttrBufferAddr__leave: + ret +_SetAttrBufferAddr: +#line 75 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" + ld (.core.SCREEN_ATTR_ADDR), hl +#line 78 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" +_SetAttrBufferAddr__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 83 "/zxbasic/src/lib/arch/zx48k/stdlib/scrbuffer.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_scrbuffer.bas b/tests/functional/arch/zx81sd/stdlib_scrbuffer.bas new file mode 100644 index 000000000..4ca94c258 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_scrbuffer.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_screen.asm b/tests/functional/arch/zx81sd/stdlib_screen.asm new file mode 100644 index 000000000..c33827280 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_screen.asm @@ -0,0 +1,2859 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_screen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl +#line 35 "/zxbasic/src/lib/arch/zx48k/stdlib/screen.bas" + push namespace core + PROC + LOCAL __SCREEN_END + LOCAL __S_SCRNS_BC + LOCAL STK_END + LOCAL RECLAIM2 + __S_SCRNS_BC EQU 2538h + STK_END EQU 5C65h + RECLAIM2 EQU 19E8h + ld bc, 4 + call __MEM_ALLOC + push hl + ld a, h + or l + jr z, __SCREEN_END + ld hl, (STK_END) + push hl + ld b, (ix+7) + ld c, (ix+5) + call __S_SCRNS_BC + call __FPSTACK_POP + pop hl + ld (STK_END), hl + pop hl + push hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld a, (de) + ld (hl), a + ex de, hl + call RECLAIM2 +__SCREEN_END: + pop hl + ld (ix-2), l + ld (ix-1), h + ENDP + pop namespace +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/screen.bas" + ld l, (ix-2) + ld h, (ix-1) + call .core.__LOADSTR +_screen__leave: + ex af, af' + exx + ld l, (ix-2) + ld h, (ix-1) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/loadstr.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/loadstr.asm" + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + push namespace core +__ILOADSTR: ; This is the indirect pointer entry HL = (HL) + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) + push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin + ld a, h + or l + ret z ; Return if NULL (No memory) + ex de, hl ; ldir takes HL as source, DE as destiny, so SWAP HL,DE + push de ; Saves destiny start + ldir ; Copies string (length number included) + pop hl ; Recovers destiny in hl as result + ret + pop namespace +#line 113 "/zxbasic/src/lib/arch/zx48k/stdlib/screen.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 115 "/zxbasic/src/lib/arch/zx48k/stdlib/screen.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_screen.bas b/tests/functional/arch/zx81sd/stdlib_screen.bas new file mode 100644 index 000000000..7fb231070 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_screen.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_scroll.asm b/tests/functional/arch/zx81sd/stdlib_scroll.asm new file mode 100644 index 000000000..480ebf542 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_scroll.asm @@ -0,0 +1,3278 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_ScrollRight: +#line 37 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + inc a + ld e, a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld d,a +LOOP1: + push hl + ld a,(hl) +AND1: + and %11100000 + ld c,a + ld a,(hl) +AND2: + and %00011111 + rra + rl b + or c + ld (hl),a + inc hl + ld a,d + and a + jr z,COLUMNN + rr b + ld b,a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + rl b +COLUMNN: + ld a,(hl) +AND3: + and %00000011 + ld c,a + ld a,(hl) + rr b + rra +AND4: + and %11111100 + or c + ld (hl),a + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %11100000 + ld c,a + ld a,b +AND6: + and %00000011 + or c + ld c,a + ld a,b +AND7: + and %00011111 + rra +AND8: + and %11111100 + or c + ld (hl),a + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + pop namespace +#line 197 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollRight__leave: + ret +_ScrollLeft: +#line 205 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, LOOP2, MASK1, MASK2, COLUMN1, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + inc a + ld e, a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND3+1),a + ld (AND6+1),a + cpl + ld (AND4+1),a + ld (AND8+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND2+1),a + ld (AND7+1),a + cpl + ld (AND1+1),a + ld (AND5+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld c, d + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld d,a +LOOP1: + push hl + ld a,(hl) +AND1: + and %00000011 + ld c,a + ld a,(hl) +AND2: + and %11111100 + rla + rl b + or c + ld (hl),a + dec hl + ld a,d + and a + jr z,COLUMN1 + rr b + ld b,a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + rl b +COLUMN1: + ld a,(hl) +AND3: + and %11100000 + ld c,a + ld a,(hl) + rr b + rla +AND4: + and %00011111 + or c + ld (hl),a + pop hl + dec e + ret z + call SP.PixelDown + jp LOOP1 +NEQ1: + ld b,(hl) + ld a,b +AND5: + and %00000011 + ld c,a + ld a,b +AND6: + and %11100000 + or c + ld c,a + ld a,b +AND7: + and %11111100 + rla +AND8: + and %00011111 + or c + ld (hl),a + dec e + ret z + call SP.PixelDown + jp NEQ1 + ENDP + pop namespace +#line 366 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollLeft__leave: + ret +_ScrollUp: +#line 375 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + push ix + inc a + ld ixL,a + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL + push hl + ld a,(hl) +AND2: + and %00011111 + ld c,a + ld a,(de) +AND1: + and %11100000 + or c + ld (de),a + inc hl + inc de + ld a,ixH + and a + jr z,COLUMNN + ld c,a + ldir +COLUMNN: + ld a,(hl) +AND4: + and %11111100 + ld c,a + ld a,(de) +AND3: + and %00000011 + or c + ld (de),a + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelDown + inc ixL + ld a,(hl) +AND7: + and %00011111 +AND8: + and %11111100 + ld c,a + ld a,(de) +AND5: + and %11100000 + ld b,a + ld a,(de) +AND6: + and %00000011 + or b + or c + ld (de),a + dec ixL + jp nz,NEQ1 + pop ix + ret + defs 32,0 +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + pop namespace +#line 545 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollUp__leave: + ret +_ScrollDown: +#line 554 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1, MASK1, MASK2, COLUMNN, NEQ1 + LOCAL AND1, AND2, AND3, AND4, AND5, AND6, AND7, AND8 + LOCAL EMPTYLINE + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + ld a, h + sub b + ret c + push ix + inc a + ld ixL,a + ld h,b + ld a,c + and 7 + inc a + ld b,a + xor a +MASK1: + rra + scf + djnz MASK1 + ld (AND1+1),a + ld (AND5+1),a + cpl + ld (AND2+1),a + ld (AND7+1),a + ld a,d + and 7 + inc a + ld b,a + xor a +MASK2: + scf + rra + djnz MASK2 + ld (AND4+1),a + ld (AND8+1),a + cpl + ld (AND3+1),a + ld (AND6+1),a + ld a,c + and %11111000 + rrca + rrca + rrca + ld b,a + ld a,d + and %11111000 + rrca + rrca + rrca + sub b + ex af,af' + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ex af,af' + jr z,NEQ1 + dec a + ld ixH,a + ld b,0 +LOOP1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL + push hl + ld a,(hl) +AND2: + and %00011111 + ld c,a + ld a,(de) +AND1: + and %11100000 + or c + ld (de),a + inc hl + inc de + ld a,ixH + and a + jr z,COLUMNN + ld c,a + ldir +COLUMNN: + ld a,(hl) +AND4: + and %11111100 + ld c,a + ld a,(de) +AND3: + and %00000011 + or c + ld (de),a + pop hl + dec ixL + jp nz,LOOP1 + pop ix + ret +NEQ1: + dec ixL + ld d,h + ld e,l + call z,EMPTYLINE + call nz,SP.PixelUp + inc ixL + ld a,(hl) +AND7: + and %00011111 +AND8: + and %11111100 + ld c,a + ld a,(de) +AND5: + and %11100000 + ld b,a + ld a,(de) +AND6: + and %00000011 + or b + or c + ld (de),a + dec ixL + jp nz,NEQ1 + pop ix + ret + defs 32,0 +EMPTYLINE: + ld hl,EMPTYLINE-32 + ENDP + pop namespace +#line 725 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollDown__leave: + ret +_ScrollRightAligned: +#line 736 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + LOCAL LOOP2 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ld a, h + sub b + ret c + inc a + ld d, a + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc +LOOP1: + push hl + ld b, e + or a +LOOP2: + rr (hl) + inc hl + djnz LOOP2 + pop hl + dec d + ret z + call SP.PixelDown + jp LOOP1 + ENDP + pop namespace +#line 792 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollRightAligned__leave: + ret +_ScrollLeftAligned: +#line 803 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + LOCAL LOOP2 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ld a, h + sub b + ret c + ld c, d + inc a + ld d, a + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc +LOOP1: + push hl + ld b, e + or a +LOOP2: + rl (hl) + dec hl + djnz LOOP2 + pop hl + dec d + ret z + call SP.PixelDown + jp LOOP1 + ENDP + pop namespace +#line 860 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollLeftAligned__leave: + ret +_ScrollUpAligned: +#line 871 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ex af, af' + ld a, h + sub b + ret c + inc a + ld d, a + ld b, h + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ld a, d + ld b, 0 + exx + ld b, a + ex af, af' + ld c, a + jp LOOP_START +LOOP1: + exx + ld d, h + ld e, l + ld c, a + call SP.PixelDown + push hl + ldir + pop hl + exx + ld a, c + LOCAL LOOP_START +LOOP_START: + djnz LOOP1 + exx + ld (hl), 0 + ld d, h + ld e, l + inc de + ld c, a + dec c + ret z + ldir + ENDP + pop namespace +#line 946 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollUpAligned__leave: + ret +_ScrollDownAligned: +#line 957 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + push namespace core + PROC + LOCAL LOOP1 + pop hl + pop bc + pop de + ex (sp), hl + ld c, a + ld a, d + sub c + ret c + srl a + srl a + srl a + inc a + ld e, a + ex af, af' + ld a, h + sub b + ret c + inc a + ld d, a + ld a, 191 + call PIXEL_ADDR + res 6, h + ld bc, (SCREEN_ADDR) + add hl, bc + ld a, d + ld b, 0 + exx + ld b, a + ex af, af' + ld c, a + jp LOOP_START +LOOP1: + exx + ld d, h + ld e, l + ld c, a + call SP.PixelUp + push hl + ldir + pop hl + exx + ld a, c + LOCAL LOOP_START +LOOP_START: + djnz LOOP1 + exx + ld (hl), 0 + ld d, h + ld e, l + inc de + ld c, a + dec c + ret z + ldir + ENDP + pop namespace +#line 1032 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +_ScrollDownAligned__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelDown.asm" + ; + ; PixelDown + ; Alvin Albrecht 2002 + ; + ; Pixel Down + ; + ; Adjusts screen address HL to move one pixel down in the display. + ; (0,0) is located at the top left corner of the screen. + ; +; enter: HL = valid screen address +; exit : Carry = moved off screen + ; Carry'= moved off current cell (needs ATTR update) + ; HL = moves one pixel down +; used : AF, HL + push namespace core +SP.PixelDown: + PROC + LOCAL leave + push de + ld de, (SCREEN_ADDR) + or a + sbc hl, de + inc h + ld a,h + and $07 + jr nz, leave + scf ; Sets carry on F', which flags ATTR must be updated + ex af, af' + ld a,h + sub $08 + ld h,a + ld a,l + add a,$20 + ld l,a + jr nc, leave + ld a,h + add a,$08 + ld h,a + cp $19 ; carry = 0 => Out of screen + jr c, leave ; returns if out of screen + ccf + pop de + ret +leave: + add hl, de ; This always sets Carry = 0 + pop de + ret + ENDP + pop namespace +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 58 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelDown.asm" +#line 1037 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/SP/PixelUp.asm" + ; + ; PixelUp + ; Alvin Albrecht 2002 + ; + ; Pixel Up + ; + ; Adjusts screen address HL to move one pixel up in the display. + ; (0,0) is located at the top left corner of the screen. + ; +; enter: HL = valid screen address +; exit : Carry = moved off screen + ; HL = moves one pixel up +; used : AF, HL + push namespace core +SP.PixelUp: + PROC + LOCAL leave + push de + ld de, (SCREEN_ADDR) + or a + sbc hl, de + ld a,h + dec h + and $07 + jr nz, leave + scf ; sets C' to 1 (ATTR update needed) + ex af, af' + ld a,$08 + add a,h + ld h,a + ld a,l + sub $20 + ld l,a + jr nc, leave + ld a,h + sub $08 + ld h,a +leave: + push af + add hl, de + pop af + pop de + ret + ENDP + pop namespace +#line 1038 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/pixel_addr.asm" + ; PIXEL_ADDR — Calcula la dirección (offset) del byte de pantalla para (B=Y, C=X) + ; Sustituye a PIXEL_ADDR EQU $22ACh (ROM Spectrum) + ; +; Interfaz idéntica a la ROM Spectrum para compatibilidad con plot.asm / draw.asm: +; Entrada: A = 191 (límite superior Y), B = Y (0=abajo, 191=arriba), C = X (0-255) +; Salida: HL = offset dentro del bitmap desde $0000 (sin base de pantalla) + ; A = X AND 7 (posición del bit, 0=izquierda/bit7, 7=derecha/bit0) +; Destruye: B (como la ROM). DEBE preservar D y E: draw.asm salva B' en D' + ; alrededor de esta llamada (ld d,b / call PIXEL_ADDR / ld b,d), igual que + ; hacía con PIXEL-ADD ($22AC) de la ROM Spectrum, que tampoco tocaba DE. + ; +; El llamador (plot.asm, draw.asm) añade la base de pantalla: + ; res 6, h ; no-op en nuestro caso (H siempre en $00-$17) + ; add hl, (SCREEN_ADDR) + ; +; Organización bitmap Spectrum interleaved: + ; V = 191 - Y (Y desde abajo → V desde arriba) + ; H = (V AND $C0)>>3 | (V AND $07) → tercio + línea en tercio + ; L = (V AND $38)<<2 | (C>>3) → fila de char*32 + columna byte + push namespace core +PIXEL_ADDR: + PROC + sub b ; A = 191 - Y (convierte coord Spectrum a offset desde arriba) + ld b, a ; B = V = 191-Y (B ya no se necesita: era la Y de entrada) + ; -- H: tercio (bits 12-11) + línea en tercio (bits 10-8) -- + and $C0 ; A = (V AND $C0) = tercio * 64 + rrca + rrca + rrca ; A = tercio * 8 + ld h, a + ld a, b + and $07 ; A = línea en tercio (0-7) + or h + ld h, a ; H = (tercio<<3) | línea_en_tercio + ; -- L: fila de carácter (bits 7-5) + columna de byte (bits 4-0) -- + ld a, b + and $38 ; A = fila_de_char * 8 + rlca + rlca ; A = fila_de_char * 32 (tras AND $38 no hay acarreo al rotar) + ld b, a + ld a, c + rrca + rrca + rrca + and $1F ; A = X / 8 (columna de byte, 0-31) + add a, b + ld l, a ; L = fila_de_char*32 + col_byte + ; -- Retornar A = X AND 7 (posición del bit dentro del byte) -- + ld a, c + and $07 ; A = X AND 7 + ret + ENDP + pop namespace +#line 1039 "/zxbasic/src/lib/arch/zx81sd/stdlib/scroll.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_scroll.bas b/tests/functional/arch/zx81sd/stdlib_scroll.bas new file mode 100644 index 000000000..8c77aa059 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_scroll.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_spectranet.asm b/tests/functional/arch/zx81sd/stdlib_spectranet.asm new file mode 100644 index 000000000..0ecc9e3b1 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_spectranet.asm @@ -0,0 +1,6392 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_mid: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+8) + ld h, (ix+9) + ex de, hl + pop hl + add hl, de + dec hl + push hl + xor a + call .core.__STRSLICE +_mid__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 + pop bc + pop bc + ex (sp), hl + exx + ret +_left: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld l, (ix+6) + ld h, (ix+7) + dec hl + push hl + xor a + call .core.__STRSLICE +_left__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 + pop bc + ex (sp), hl + exx + ret +_right: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix+4) + ld h, (ix+5) + call .core.__STRLEN + push hl + ld l, (ix+6) + ld h, (ix+7) + ex de, hl + pop hl + or a + sbc hl, de + push hl + ld hl, 65534 + push hl + xor a + call .core.__STRSLICE +_right__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 + pop bc + ex (sp), hl + exx + ret +_strpos2: + push ix + ld ix, 0 + add ix, sp + ld hl, -10 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 9 + ld d, h + ld e, l + inc de + ldir + ld h, (ix+5) + ld l, (ix+4) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + call .core.__STRLEN + ld (ix-2), l + ld (ix-1), h + ld h, (ix+7) + ld l, (ix+6) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + call .core.__STRLEN + ld (ix-4), l + ld (ix-3), h + ld l, (ix-4) + ld h, (ix-3) + push hl + ld l, (ix-2) + ld h, (ix-1) + pop de + or a + sbc hl, de + jp nc, .LABEL.__LABEL1 + ld hl, 65535 + jp _strpos2__leave +.LABEL.__LABEL1: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + ex de, hl + pop hl + or a + sbc hl, de + ld (ix-6), l + ld (ix-5), h + ld l, (ix-4) + ld h, (ix-3) + dec hl + ld (ix-8), l + ld (ix-7), h + ld (ix-10), 0 + ld (ix-9), 0 + jp .LABEL.__LABEL2 +.LABEL.__LABEL5: + ld h, (ix+7) + ld l, (ix+6) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + push hl + ld h, (ix+5) + ld l, (ix+4) + ld c, (hl) + inc hl + ld h, (hl) + ld l, c + push hl + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + add hl, de + push hl + xor a + call .core.__STRSLICE + ex de, hl + pop hl + ld a, 2 + call .core.__STREQ + or a + jp z, .LABEL.__LABEL8 + ld l, (ix-10) + ld h, (ix-9) + jp _strpos2__leave +.LABEL.__LABEL8: +.LABEL.__LABEL6: + ld l, (ix-10) + ld h, (ix-9) + inc hl + ld (ix-10), l + ld (ix-9), h +.LABEL.__LABEL2: + ld l, (ix-10) + ld h, (ix-9) + push hl + ld l, (ix-6) + ld h, (ix-5) + pop de + or a + sbc hl, de + jp nc, .LABEL.__LABEL5 +.LABEL.__LABEL4: + ld hl, 65535 +_strpos2__leave: + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_strpos: + push ix + ld ix, 0 + add ix, sp + push ix + pop hl + ld de, 6 + add hl, de + push hl + push ix + pop hl + ld de, 4 + add hl, de + push hl + call _strpos2 +_strpos__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_inStr: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push ix + pop hl + ld de, 6 + add hl, de + push hl + push ix + pop hl + ld de, 4 + add hl, de + push hl + call _strpos2 + ld (ix-2), l + ld (ix-1), h + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 65535 + pop hl + or a + sbc hl, de + ld a, h + or l + sub 1 + sbc a, a + inc a +_inStr__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_ucase2: +#line 125 "/zxbasic/src/lib/arch/zx48k/stdlib/string.bas" + PROC + LOCAL __LOOP + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ld a, h + or l + ret z + ld c, (hl) + inc hl + ld b, (hl) +__LOOP: + inc hl + ld a, b + or c + ret z + ld a, (hl) + dec bc + cp 'a' + jp c, __LOOP + cp 123 + jp nc, __LOOP + res 5,(hl) + jp __LOOP + ENDP +#line 166 "/zxbasic/src/lib/arch/zx48k/stdlib/string.bas" +_ucase2__leave: + ret +_ucase: + push ix + ld ix, 0 + add ix, sp + push ix + pop hl + ld de, 4 + add hl, de + ld a, 1 + call _ucase2 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR +_ucase__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 +_lcase2: +#line 186 "/zxbasic/src/lib/arch/zx48k/stdlib/string.bas" + PROC + LOCAL __LOOP + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ld a, h + or l + ret z + ld c, (hl) + inc hl + ld b, (hl) +__LOOP: + inc hl + ld a, b + or c + ret z + ld a, (hl) + dec bc + cp 'A' + jp c, __LOOP + cp 91 + jp nc, __LOOP + set 5,(hl) + jp __LOOP + ENDP +#line 227 "/zxbasic/src/lib/arch/zx48k/stdlib/string.bas" +_lcase2__leave: + ret +_lcase: + push ix + ld ix, 0 + add ix, sp + push ix + pop hl + ld de, 4 + add hl, de + ld a, 1 + call _lcase2 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR +_lcase__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 +_ltrim: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld l, (ix+6) + ld h, (ix+7) + call .core.__STRLEN + ld (ix-8), l + ld (ix-7), h + ld l, (ix-8) + ld h, (ix-7) + ld a, h + or l + jp nz, .LABEL.__LABEL10 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR + jp _ltrim__leave +.LABEL.__LABEL10: + ld l, (ix-8) + ld h, (ix-7) + dec hl + ld (ix-4), l + ld (ix-3), h + ld l, (ix+4) + ld h, (ix+5) + call .core.__STRLEN + ld (ix-6), l + ld (ix-5), h +.LABEL.__LABEL11: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-6) + ld h, (ix-5) + ex de, hl + pop hl + or a + sbc hl, de + sbc a, a + push af + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + ex de, hl + pop hl + add hl, de + push hl + xor a + call .core.__STRSLICE + ex de, hl + pop hl + ld a, 2 + call .core.__STREQ + ld h, a + pop af + or a + jr z, .LABEL.__LABEL33 + ld a, h +.LABEL.__LABEL33: + or a + jp z, .LABEL.__LABEL12 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + add hl, de + ld (ix-2), l + ld (ix-1), h + jp .LABEL.__LABEL11 +.LABEL.__LABEL12: + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld hl, 65534 + push hl + xor a + call .core.__STRSLICE +_ltrim__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_rtrim: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld l, (ix+6) + ld h, (ix+7) + call .core.__STRLEN + ld (ix-8), l + ld (ix-7), h + ld l, (ix+4) + ld h, (ix+5) + call .core.__STRLEN + ld (ix-6), l + ld (ix-5), h + ld l, (ix-8) + ld h, (ix-7) + ld a, h + or l + sub 1 + sbc a, a + push af + ld l, (ix-6) + ld h, (ix-5) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + or a + sbc hl, de + sbc a, a + pop de + or d + jp z, .LABEL.__LABEL14 + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR + jp _rtrim__leave +.LABEL.__LABEL14: + ld l, (ix-8) + ld h, (ix-7) + dec hl + ld (ix-4), l + ld (ix-3), h + ld l, (ix-6) + ld h, (ix-5) + dec hl + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL15: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + pop de + call .core.__LEI16 + push af + ld l, (ix+6) + ld h, (ix+7) + push hl + ld l, (ix+4) + ld h, (ix+5) + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-4) + ld h, (ix-3) + ex de, hl + pop hl + or a + sbc hl, de + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + xor a + call .core.__STRSLICE + ex de, hl + pop hl + ld a, 2 + call .core.__STREQ + ld h, a + pop af + or a + jr z, .LABEL.__LABEL34 + ld a, h +.LABEL.__LABEL34: + or a + jp z, .LABEL.__LABEL16 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix-8) + ld h, (ix-7) + ex de, hl + pop hl + or a + sbc hl, de + ld (ix-2), l + ld (ix-1), h + jp .LABEL.__LABEL15 +.LABEL.__LABEL16: + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 0 + pop hl + call .core.__LTI16 + or a + jp z, .LABEL.__LABEL18 + ld hl, .LABEL.__LABEL19 + call .core.__LOADSTR + jp _rtrim__leave +.LABEL.__LABEL18: + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + xor a + call .core.__STRSLICE +_rtrim__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_trim: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+6) + ld h, (ix+7) + call .core.__LOADSTR + push hl + ld l, (ix+6) + ld h, (ix+7) + call .core.__LOADSTR + push hl + ld l, (ix+4) + ld h, (ix+5) + call .core.__LOADSTR + push hl + call _rtrim + push hl + call _ltrim +_trim__leave: + ex af, af' + exx + ld l, (ix+4) + ld h, (ix+5) + call .core.__MEM_FREE + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + ex (sp), hl + exx + ret +_isdigit: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld hl, 0 + push hl + xor a + call .core.__STRSLICE + ld a, 1 + call .core.__ASC + ld (ix-1), a + sub 48 + ccf + sbc a, a + push af + ld a, (ix-1) + push af + ld a, 57 + pop hl + sub h + ccf + sbc a, a + ld h, a + pop af + or a + jr z, .LABEL.__LABEL35 + ld a, h +.LABEL.__LABEL35: + sub 1 + sbc a, a + inc a +_isdigit__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 +_isletter: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + inc sp + ld l, (ix+4) + ld h, (ix+5) + push hl + ld hl, 0 + push hl + ld hl, 0 + push hl + xor a + call .core.__STRSLICE + ld a, 1 + call .core.__ASC + ld (ix-1), a + sub 97 + ccf + sbc a, a + push af + ld a, (ix-1) + push af + ld a, 122 + pop hl + sub h + ccf + sbc a, a + ld h, a + pop af + or a + jr z, .LABEL.__LABEL36 + ld a, h +.LABEL.__LABEL36: + push af + ld a, (ix-1) + sub 65 + ccf + sbc a, a + push af + ld a, (ix-1) + push af + ld a, 90 + pop hl + sub h + ccf + sbc a, a + ld h, a + pop af + or a + jr z, .LABEL.__LABEL37 + ld a, h +.LABEL.__LABEL37: + pop de + or d + sub 1 + sbc a, a + inc a +_isletter__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 +_SNETsocket: +#line 59 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld c, a + ld hl, Spectranet.SOCKET + call Spectranet.HLCALL +#line 64 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETsocket__leave: + ret +_SNETbind: +#line 68 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + ex (sp), hl + ex de, hl + ld hl, Spectranet.BIND + call Spectranet.HLCALL +#line 75 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETbind__leave: + ret +_SNETlisten: +#line 79 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.LISTEN + call Spectranet.HLCALL +#line 83 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETlisten__leave: + ret +_SNETaccept: +#line 87 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.ACCEPT + call Spectranet.HLCALL +#line 91 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETaccept__leave: + ret +_SNETconnect: +#line 95 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.CONNECT + push de + inc de + inc de + call Spectranet.HLCALL + pop hl + ex af, af' + call __MEM_FREE + ex af, af' +#line 110 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETconnect__leave: + ret +_SNETclose: +#line 114 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.CLOSE + call Spectranet.HLCALL +#line 118 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETclose__leave: + ret +_SNETrecv: +#line 122 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.RECV + call Spectranet.HLCALL +#line 130 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETrecv__leave: + ret +_SNETsend: +#line 134 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.SEND + call Spectranet.HLCALL +#line 142 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETsend__leave: + ret +_SNETpollfd: +#line 146 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.POLLFD + call Spectranet.HLCALL + ld a, 0 + ret z + ld c, a +#line 153 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETpollfd__leave: + ret +_SNETpeekUinteger: +#line 160 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ex de, hl + ld hl, Spectranet.PAGEIN + call Spectranet.HLCALL + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + ld hl, Spectranet.PAGEOUT + call Spectranet.HLCALL + ex de, hl +#line 172 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETpeekUinteger__leave: + ret +_SNETmount: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld hl, -8 + ld de, .LABEL.__LABEL38 + ld bc, 10 + call .core.__ALLOC_LOCAL_ARRAY + ld l, (ix+6) + ld h, (ix+7) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 6 + call .core.__PSTORE_STR2 + ld l, (ix+8) + ld h, (ix+9) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 8 + call .core.__PSTORE_STR2 + ld l, (ix+10) + ld h, (ix+11) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 10 + call .core.__PSTORE_STR2 + ld l, (ix+12) + ld h, (ix+13) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 12 + call .core.__PSTORE_STR2 + ld l, (ix+14) + ld h, (ix+15) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 14 + call .core.__PSTORE_STR2 + ld l, (ix-6) + ld h, (ix-5) + push hl + push ix + pop hl + ld de, 6 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + inc hl + inc hl + push hl + push ix + pop hl + ld de, 8 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + ld de, 4 + add hl, de + push hl + push ix + pop hl + ld de, 10 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + ld de, 6 + add hl, de + push hl + push ix + pop hl + ld de, 12 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld l, (ix-6) + ld h, (ix-5) + ld de, 8 + add hl, de + push hl + push ix + pop hl + ld de, 14 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ex de, hl + pop hl + ld (hl), e + inc hl + ld (hl), d + ld hl, 0 + push hl + push ix + pop hl + ld de, -8 + add hl, de + call .core.__ARRAY + ld (ix-2), l + ld (ix-1), h +#line 203 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld a, (ix + 5) + push ix + push hl + pop ix + ld hl, Spectranet.MOUNT + call Spectranet.HLCALL + pop ix +#line 212 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETmount__leave: + ex af, af' + exx + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ld l, (ix+8) + ld h, (ix+9) + call .core.__MEM_FREE + ld l, (ix+10) + ld h, (ix+11) + call .core.__MEM_FREE + ld l, (ix+12) + ld h, (ix+13) + call .core.__MEM_FREE + ld l, (ix+14) + ld h, (ix+15) + call .core.__MEM_FREE + ld l, (ix-6) + ld h, (ix-5) + call .core.__MEM_FREE + ex af, af' + ld hl, 12 +__EXIT_FUNCTION: + ld sp, ix + pop ix + pop de + add hl, sp + ld sp, hl + push de + exx + ret +_SNETcurrMPoint: + push ix + ld ix, 0 + add ix, sp + call .core.COPY_ATTR + ld a, 7 + call .core.INK_TMP + ld a, 2 + call .core.PAPER_TMP + ld hl, 4097 + call _SNETpeekUinteger + ld a, l + call .core.__PRINTU8 + call .core.PRINT_EOL + ld hl, 4097 + call _SNETpeekUinteger + ld a, l +_SNETcurrMPoint__leave: + ld sp, ix + pop ix + ret +_SNETsetmountpt: +#line 228 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.SETMOUNTPOINT + call Spectranet.HLCALL +#line 232 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETsetmountpt__leave: + ret +_SNETumount: +#line 242 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.UMOUNT + call Spectranet.HLCALL +#line 246 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETumount__leave: + ret +_SNETopen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix+6) + ld h, (ix+7) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 6 + call .core.__PSTORE_STR2 + push ix + pop hl + ld de, 6 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ld (ix-2), l + ld (ix-1), h +#line 268 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld a, (ix + 5) + ld e, (ix + 8) + ld d, (ix + 9) + ld c, (ix + 10) + ld b, (ix + 11) + push ix + ld ix, Spectranet.OPEN + call Spectranet.IXCALL + pop ix +#line 280 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETopen__leave: + ex af, af' + exx + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + pop bc + ex (sp), hl + exx + ret +_SNETfread: +#line 292 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ld hl, Spectranet.READ + call Spectranet.HLCALL + ld h, b + ld c, l + ret nc + ld (23610), a +#line 304 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfread__leave: + ret +_SNETfwrite: +#line 316 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop de + pop bc + push hl + ex de, hl + push ix + ld ix, Spectranet.WRITE + call Spectranet.IXCALL + pop ix + ld h, b + ld c, l + ret nc + ld (23610), a +#line 331 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfwrite__leave: + ret +_SNETfopen: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld hl, .LABEL.__LABEL21 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL23 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 1 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL23: + ld hl, .LABEL.__LABEL24 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL26 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 256 + pop hl + call .core.__BOR16 + push hl + ld de, 512 + pop hl + call .core.__BOR16 + push hl + ld de, 2 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL26: + ld hl, .LABEL.__LABEL27 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL29 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 8 + pop hl + call .core.__BOR16 + push hl + ld de, 256 + pop hl + call .core.__BOR16 + push hl + ld de, 2 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL29: + ld hl, .LABEL.__LABEL30 + call .core.__LOADSTR + push hl + ld l, (ix+8) + ld h, (ix+9) + call .core.__LOADSTR + push hl + call _inStr + or a + jp z, .LABEL.__LABEL32 + ld l, (ix-2) + ld h, (ix-1) + push hl + ld de, 3 + pop hl + call .core.__BOR16 + ld (ix-2), l + ld (ix-1), h +.LABEL.__LABEL32: + ld hl, 438 + push hl + ld l, (ix-2) + ld h, (ix-1) + push hl + ld l, (ix+6) + ld h, (ix+7) + call .core.__LOADSTR + push hl + ld a, (ix+5) + push af + call _SNETopen +_SNETfopen__leave: + ex af, af' + exx + ld l, (ix+6) + ld h, (ix+7) + call .core.__MEM_FREE + ld l, (ix+8) + ld h, (ix+9) + call .core.__MEM_FREE + ex af, af' + exx + ld sp, ix + pop ix + exx + pop hl + pop bc + pop bc + ex (sp), hl + exx + ret +_SNETfclose: +#line 366 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + ld hl, Spectranet.CLOSE + call Spectranet.HLCALL + ret c + xor a +#line 372 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfclose__leave: + ret +_SNETfseek: +#line 383 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + pop hl + pop bc + ld c, b + pop de + ex (sp), hl + ex de, hl + push ix + ld ix, Spectranet.LSEEK + call Spectranet.IXCALL + pop ix + ret c + xor a +#line 398 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETfseek__leave: + ret +_SNETunlink: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + ld l, (ix+4) + ld h, (ix+5) + push hl + ld de, .LABEL.__LABEL20 + pop hl + call .core.__ADDSTR + ld d, h + ld e, l + ld bc, 4 + call .core.__PSTORE_STR2 + push ix + pop hl + ld de, 4 + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + inc hl + inc hl + ld (ix-2), l + ld (ix-1), h +#line 411 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" + PROC + LOCAL CONT + push ix + ld ix, Spectranet.UNLINK + call Spectranet.IXCALL + pop ix + jr c, CONT + xor a +CONT: + ENDP +#line 423 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +_SNETunlink__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 +.LABEL.__LABEL19: + DEFW 0000h +.LABEL.__LABEL20: + DEFW 0001h + DEFB 00h +.LABEL.__LABEL21: + DEFW 0001h + DEFB 72h +.LABEL.__LABEL24: + DEFW 0001h + DEFB 77h +.LABEL.__LABEL27: + DEFW 0001h + DEFB 61h +.LABEL.__LABEL30: + DEFW 0001h + DEFB 2Bh + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ... + ; +; zx81sd override: the only change from zx48k's version is + ; LBOUND_PTR's storage address. The original uses the Spectrum ROM's + ; MEMBOT system variable (23698, safe scratch RAM on real hardware) -- + ; on zx81sd that address falls inside the program's own compiled code + ; (block 2, $4000-$5FFF) instead of a real sysvar, silently corrupting + ; it on every multi-dimensional array access. ARRAY_SCRATCH (sysvars.asm) + ; is a dedicated 8-byte scratch area in the zx81sd sysvar block instead. +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + ;; Performs a faster multiply for little 16bit numbs +#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 3 "/zxbasic/src/lib/arch/zx48k/runtime/arith/fmul16.asm" + push namespace core +__FMUL16: + 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 +1: + add hl, de + djnz 1b + ret + pop namespace +#line 27 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 28 "/zxbasic/src/lib/arch/zx81sd/runtime/array/array.asm" +#line 32 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 ARRAY_SCRATCH ; zx81sd: dedicated scratch, not MEMBOT + 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 74 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 124 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 __FMUL16 ; HL <= HL * DE mod 65536 + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 154 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 164 "/zxbasic/src/lib/arch/zx81sd/runtime/array/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 + ENDP + pop namespace +#line 458 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 459 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/asc.asm" + ; Returns the ascii code for the given str +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 3 "/zxbasic/src/lib/arch/zx48k/runtime/asc.asm" + push namespace core +__ASC: + PROC + LOCAL __ASC_END + ex af, af' ; Saves free_mem flag + ld a, h + or l + ret z ; NULL? return + ld c, (hl) + inc hl + ld b, (hl) + ld a, b + or c + jr z, __ASC_END ; No length? return + inc hl + ld a, (hl) + dec hl +__ASC_END: + dec hl + ex af, af' + or a + call nz, __MEM_FREE ; Free memory if needed + ex af, af' ; Recover result + ret + ENDP + pop namespace +#line 460 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/bitwise/bor16.asm" +; vim:ts=4:et: + ; FASTCALL bitwise or 16 version. + ; result in HL +; __FASTCALL__ version (operands: A, H) + ; Performs 16bit or 16bit and returns the boolean +; Input: HL, DE +; Output: HL <- HL OR DE + push namespace core +__BOR16: + ld a, h + or d + ld h, a + ld a, l + or e + ld l, a + ret + pop namespace +#line 461 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lei16.asm" + push namespace core +__LEI16: + PROC + LOCAL checkParity + or a + sbc hl, de + ld a, 1 + ret z + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 462 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti16.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/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 2 "/zxbasic/src/lib/arch/zx48k/runtime/cmp/lti16.asm" + push namespace core +__LTI16: ; Test 8 bit values HL < DE + ; Returns result in A: 0 = False, !0 = True + PROC + LOCAL checkParity + or a + sbc hl, de + jp po, checkParity + ld a, h + xor 0x80 +checkParity: + ld a, 0 ; False + ret p + inc a ; True + ret + ENDP + pop namespace +#line 463 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/copy_attr.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/sposn.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/runtime/print.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx81sd/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 ' ' + jp c, __PRINT_SPECIAL ; Characters below ' ' are special ones (> 127 bytes away) + 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/zx81sd/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 se incluye como rutina propia (po_gr_1.asm), no desde ROM Spectrum +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/po_gr_1.asm" + ; PO_GR_1 — Genera el patrón de bits para caracteres gráficos 128-143 + ; Sustituye a PO-GR-1 ($0B38, ROM Spectrum). Mismo algoritmo que la ROM. + ; + ; Los caracteres gráficos Spectrum (CHR$ 128 a CHR$ 143) son bloques + ; de 2×2 cuadrantes. El nibble bajo del código determina qué cuadrantes +; están encendidos, con el mapeo EXACTO de la ROM (PO-GR-2, $0B3E): + ; + ; bit 0 = cuadrante superior DERECHO → $0F en filas 0-3 + ; bit 1 = cuadrante superior IZQUIERDO → $F0 en filas 0-3 + ; bit 2 = cuadrante inferior DERECHO → $0F en filas 4-7 + ; bit 3 = cuadrante inferior IZQUIERDO → $F0 en filas 4-7 + ; + ; (CHR$(129) = ▝, CHR$(130) = ▘, CHR$(143) = bloque macizo.) + ; +; Entrada: B = código de carácter (128-143), solo se usan bits 3-0 +; Salida: MEM0 (8 bytes) = patrón del carácter; HL = MEM0 +; Destruye: A, B, C + push namespace core +PO_GR_1: + PROC + LOCAL PO_GR_HALF, PO_GR_FILL + ld a, b + and $0F + ld b, a ; B = bits de cuadrante + ld hl, MEM0 + call PO_GR_HALF ; filas 0-3 (bits 0-1) + call PO_GR_HALF ; filas 4-7 (bits 2-3) + ld hl, MEM0 ; devuelve el puntero al patrón + ret + ; Construye media celda (4 filas iguales) a partir de los dos bits + ; bajos de B, desplazándolos fuera. Igual que PO-GR-2 de la ROM. +PO_GR_HALF: + rr b ; bit par → carry + sbc a, a ; $00 o $FF + and $0F ; mitad derecha + ld c, a + rr b ; bit impar → carry + sbc a, a + and $F0 ; mitad izquierda + or c ; byte de la fila completo + ld c, 4 +PO_GR_FILL: + ld (hl), a + inc hl + dec c + jr nz, PO_GR_FILL + ret + ENDP + pop namespace +#line 117 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__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 + ld a, (FLAGS2) + bit 2, a + call nz, __BOLD +#line 143 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + ld a, (FLAGS2) + bit 4, a + call nz, __ITALIC +#line 149 "/zxbasic/src/lib/arch/zx81sd/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 214 "/zxbasic/src/lib/arch/zx81sd/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 + jp __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 + jp __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jp __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jp __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jp __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jp __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jp __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 358 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 368 "/zxbasic/src/lib/arch/zx81sd/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 389 "/zxbasic/src/lib/arch/zx81sd/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 417 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __SCROLL_SCR +; En zx81sd el scroll se hace SIEMPRE con la implementacion por buffer: + ; la variante del zx48k ("__SCROLL_SCR EQU 0DFEh") delega en la rutina + ; CL-SC-ALL de la ROM del Spectrum, que aqui no esta mapeada — ese CALL +; ejecutaria codigo arbitrario del programa (bug cazado con flights.bas: + ; el primer PRINT que desbordaba la pantalla saltaba a la linea BASIC + ; que casualmente ocupara $0DFE). +__SCROLL_SCR: ;; Scrolls screen and attrs 1 row up + ld de, (SCREEN_ADDR) + ld b, 3 +3: + push bc + ld a, 8 +1: + ld hl, 32 + add hl, de + ld bc, 32 * 7 + push de + ldir + pop de + inc d + dec a + jr nz, 1b + push hl + ld bc, -32 - 256 * 7 + add hl, bc + ex de, hl + ld a, 8 +2: + ld bc, 32 + push hl + push de + ldir + pop de + pop hl + inc d + inc h + dec a + jr nz, 2b + pop de + pop bc + djnz 3b + dec de + ld h, d + ld l, e + ld a, 8 +3: + push hl + push de + ld (hl), b + dec de + ld bc, 31 + lddr + pop de + pop hl + dec d + dec h + dec a + jr nz, 3b + ld de, (SCREEN_ATTR_ADDR) + ld hl, 32 + add hl, de + ld bc, 32 * 23 + ldir + ld h, d + ld l, e + ld a, (ATTR_P) + ld (hl), a + inc de + ld bc, 31 + ldir + ret +#line 496 "/zxbasic/src/lib/arch/zx81sd/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 552 "/zxbasic/src/lib/arch/zx81sd/runtime/print.asm" + LOCAL __PRINT_BOLD2 +#line 558 "/zxbasic/src/lib/arch/zx81sd/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/zx48k/runtime/copy_attr.asm" +#line 4 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 464 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/loadstr.asm" + ; Loads a string (ptr) from HL + ; and duplicates it on dynamic memory again + ; Finally, it returns result pointer in HL + push namespace core +__ILOADSTR: ; This is the indirect pointer entry HL = (HL) + ld a, h + or l + ret z + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +__LOADSTR: ; __FASTCALL__ entry + ld a, h + or l + ret z ; Return if NULL + ld c, (hl) + inc hl + ld b, (hl) + dec hl ; BC = LEN(a$) + inc bc + inc bc ; BC = LEN(a$) + 2 (two bytes for length) + push hl + push bc + call __MEM_ALLOC + pop bc ; Recover length + pop de ; Recover origin + ld a, h + or l + ret z ; Return if NULL (No memory) + ex de, hl ; ldir takes HL as source, DE as destiny, so SWAP HL,DE + push de ; Saves destiny start + ldir ; Copies string (length number included) + pop hl ; Recovers destiny in hl as result + ret + pop namespace +#line 466 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.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/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/zx48k/runtime/printi8.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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/zx48k/runtime/printu8.asm" +#line 470 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" +; vim:ts=4:et:sw=4 + ; + ; Stores an string (pointer to the HEAP by DE) into the address pointed + ; by (IX + BC). No new copy of the string is created into the HEAP, since + ; it's supposed it's already created (temporary string) + ; +#line 1 "/zxbasic/src/lib/arch/zx48k/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. + 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 9 "/zxbasic/src/lib/arch/zx48k/runtime/pstorestr2.asm" + push namespace core +__PSTORE_STR2: + push ix + pop hl + add hl, bc + jp __STORE_STR2 + pop namespace +#line 471 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/spectranet.inc" + ;The MIT License + ; + ;Copyright (c) 2008 Dylan Smith + ; + ;Permission is hereby granted, free of charge, to any person obtaining a copy + ;of this software and associated documentation files (the "Software"), to deal + ;in the Software without restriction, including without limitation the rights + ;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + ;copies of the Software, and to permit persons to whom the Software is +;furnished to do so, subject to the following conditions: + ; + ;The above copyright notice and this permission notice shall be included in + ;all copies or substantial portions of the Software. + ; + ;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + ;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + ;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + ;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + ;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + ;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + ;THE SOFTWARE. + ; This file can be included in assembly language programs to give + ; symbolic access to the public jump table entry points. + ; Avoid double inclusion + NAMESPACE Spectranet ; NAME PREFIX to avoid naming clash + ; Hardware page-in entry points + MODULECALL equ $3FF8 + MODULECALL_NOPAGE equ $28 + PAGEIN equ $3FF9 + PAGEOUT equ $007C + HLCALL equ $3FFA + IXCALL equ $3FFD + ; Port defines + CTRLREG equ $033B + CPLDINFO equ $023B + ; Jump table entry points + SOCKET equ $3E00 ; Allocate a socket + CLOSE equ $3E03 ; Close a socket + LISTEN equ $3E06 ; Listen for incoming connections + ACCEPT equ $3E09 ; Accept an incoming connection + BIND equ $3E0C ; Bind a local address to a socket + CONNECT equ $3E0F ; Connect to a remote host + SEND equ $3E12 ; Send data + RECV equ $3E15 ; Receive data + SENDTO equ $3E18 ; Send data to an address + RECVFROM equ $3E1B ; Receive data from an address + POLL equ $3E1E ; Poll a list of sockets + POLLALL equ $3E21 ; Poll all open sockets + POLLFD equ $3E24 ; Poll a single socket + GETHOSTBYNAME equ $3E27 ; Look up a hostname + PUTCHAR42 equ $3E2A ; 42 column print write a character + PRINT42 equ $3E2D ; 42 column print a null terminated string + CLEAR42 equ $3E30 ; Clear the screen and reset 42-col print + SETPAGEA equ $3E33 ; Sets page area A + SETPAGEB equ $3E36 ; Sets page area B + LONG2IPSTRING equ $3E39 ; Convert a 4 byte big endian long to an IP + IPSTRING2LONG equ $3E3C ; Convert an IP to a 4 byte big endian long + ITOA8 equ $3E3F ; Convert a byte to ascii + RAND16 equ $3E42 ; 16 bit PRNG + REMOTEADDRESS equ $3E45 ; Fill struct sockaddr_in + IFCONFIG_INET equ $3E48 ; Set IPv4 address + IFCONFIG_NETMASK equ $3E4B ; Set netmask + IFCONFIG_GW equ $3E4E ; Set gateway + INITHW equ $3E51 ; Set the MAC address and initial hw registers + GETHWADDR equ $3E54 ; Read the MAC address + DECONFIG equ $3E57 ; Deconfigure inet, netmask and gateway + MAC2STRING equ $3E5A ; Convert 6 byte MAC address to a string + STRING2MAC equ $3E5D ; Convert a hex string to a 6 byte MAC address + ITOH8 equ $3E60 ; Convert accumulator to hex string + HTOI8 equ $3E63 ; Convert hex string to byte in A + GETKEY equ $3E66 ; Get a key from the keyboard, and put it in A + KEYUP equ $3E69 ; Wait for key release + INPUTSTRING equ $3E6C ; Read a string into buffer at DE + GET_IFCONFIG_INET equ $3E6F ; Gets the current IPv4 address + GET_IFCONFIG_NETMASK equ $3E72 ; Gets the current netmask + GET_IFCONFIG_GW equ $3E75 ; Gets the current gateway address + SETTRAP equ $3E78 ; Sets the programmable trap + DISABLETRAP equ $3E7B ; Disables the programmable trap + ENABLETRAP equ $3E7E ; Enables the programmable trap + PUSHPAGEA equ $3E81 ; Pages a page into area A, pushing the old one + POPPAGEA equ $3E84 ; Restores the previous page in area A + PUSHPAGEB equ $3E87 ; Pages into area B pushing the old one + POPPAGEB equ $3E8A ; Restores the previous page in area B + PAGETRAPRETURN equ $3E8D ; Returns from a trap to page area B + TRAPRETURN equ $3E90 ; Returns from a trap that didn't page area B + ADDBASICEXT equ $3E93 ; Adds a BASIC command + STATEMENT_END equ $3E96 ; Check for statement end, exit at syntax time + EXIT_SUCCESS equ $3E99 ; Use this to exit successfully after cmd + PARSE_ERROR equ $3E9C ; Use this to exit to BASIC with a parse error + RESERVEPAGE equ $3E9F ; Reserve a page of static RAM + FREEPAGE equ $3EA2 ; Free a page of static RAM + REPORTERR equ $3EA5 ; report an error via BASIC + ; Filesystem functions + MOUNT equ $3EA8 + UMOUNT equ $3EAB + OPENDIR equ $3EAE + OPEN equ $3EB1 + UNLINK equ $3EB4 + MKDIR equ $3EB7 + RMDIR equ $3EBA + SIZE equ $3EBD + FREE equ $3EC0 + STAT equ $3EC3 + CHMOD equ $3EC6 + READ equ $3EC9 + WRITE equ $3ECC + LSEEK equ $3ECF + VCLOSE equ $3ED2 + VPOLL equ $3ED5 + READDIR equ $3ED8 + CLOSEDIR equ $3EDB + CHDIR equ $3EDE + GETCWD equ $3EE1 + RENAME equ $3EE4 + SETMOUNTPOINT equ $3EE7 + FREEMOUNTPOINT equ $3EEA + RESALLOC equ $3EED + ; Definitions + ALLOCFD equ 1 + FREEFD equ 0 + ALLOCDIRHND equ 3 + FREEDIRHND equ 2 + ; POLL status bits + BIT_RECV equ 2 + BIT_DISCON equ 1 + BIT_CONN equ 0 + NAMESPACE DEFAULT ; Clears namespace +#line 143 "/zxbasic/src/lib/arch/zx48k/runtime/spectranet.inc" +#line 472 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strcat.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/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/zx48k/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 473 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/string.asm" + ; String library + push namespace core +__STR_ISNULL: ; Returns A = FF if HL is 0, 0 otherwise + ld a, h + or l + sub 1 ; Only CARRY if HL is NULL + sbc a, a ; Only FF if HL is NULL (0 otherwise) + ret +__STRCMP: ; Compares strings at HL (a$), DE (b$) + ; Returns 0 if EQual, -1 if HL < DE, +1 if HL > DE + ; A register is preserved and returned in A' + PROC ; __FASTCALL__ + LOCAL __STRCMPZERO + LOCAL __STRCMPEXIT + LOCAL __STRCMPLOOP + LOCAL __EQULEN1 + LOCAL __HLZERO + ex af, af' ; Saves current A register in A' (it's used by STRXX comparison functions) + push hl + call __STRLEN + ld a, h + or l + pop hl + jr z, __HLZERO ; if HL == "", go to __HLZERO + push de + ex de, hl + call __STRLEN + ld a, h + or l + ld a, 1 + ex de, hl ; Recovers HL + pop de + ret z ; Returns +1 if HL != "" AND DE == "" + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = LEN(a$) + push hl ; HL = &a$, saves it + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ex de, hl ; HL = LEN(b$), de = &b$ + ; At this point Carry is cleared, and A reg. = 1 + sbc hl, bc ; Carry if len(a$)[BC] > len(b$)[HL] + ld a, 0 + jr z, __EQULEN1 ; Jumps if len(a$)[BC] = len(b$)[HL] : A = 0 + dec a + jr nc, __EQULEN1 ; Jumps if len(a$)[BC] < len(b$)[HL] : A = 1 + adc hl, bc ; Restores HL + ld a, 1 ; Signals len(a$)[BC] > len(b$)[HL] : A = 1 + ld b, h + ld c, l +__EQULEN1: + pop hl ; Recovers A$ pointer + push af ; Saves A for later (Value to return if strings reach the end) + ld a, b + or c + jr z, __STRCMPZERO ; empty string being compared + ; At this point: BC = lesser length, DE and HL points to b$ and a$ chars respectively +__STRCMPLOOP: + ld a, (de) + cpi + jr nz, __STRCMPEXIT ; (HL) != (DE). Examine carry + jp po, __STRCMPZERO ; END of string (both are equal) + inc de + jp __STRCMPLOOP +__STRCMPZERO: + pop af ; This is -1 if len(a$) < len(b$), +1 if len(b$) > len(a$), 0 otherwise + ret +__STRCMPEXIT: ; Sets A with the following value + dec hl ; Get back to the last char + cp (hl) + sbc a, a ; A = -1 if carry => (DE) < (HL); 0 otherwise (DE) > (HL) + cpl ; A = -1 if (HL) < (DE), 0 otherwise + add a, a ; A = A * 2 (thus -2 or 0) + inc a ; A = A + 1 (thus -1 or 1) + pop bc ; Discard top of the stack + ret +__HLZERO: + ex de, hl + call __STRLEN + ld a, h + or l + ret z ; Returns 0 (EQ) if HL == DE == "" + ld a, -1 + ret ; Returns -1 if HL == "" and DE != "" + ENDP + ; The following routines perform string comparison operations (<, >, ==, etc...) + ; On return, A will contain 0 for False, other value for True + ; Register A' will determine whether the incoming strings (HL, DE) will be freed + ; from dynamic memory on exit: + ; Bit 0 => 1 means HL will be freed. + ; Bit 1 => 1 means DE will be freed. +__STREQ: ; Compares a$ == b$ (HL = ptr a$, DE = ptr b$). Returns FF (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + sub 1 + sbc a, a ; 0 if A register was 0, 0xFF if A was 1 or -1 + jp __FREE_STR +__STRNE: ; Compares a$ != b$ (HL = ptr a$, DE = ptr b$). Returns 1 (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + jp __FREE_STR +__STRLT: ; Compares a$ < b$ (HL = ptr a$, DE = ptr b$). Returns FE (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + or a + jp z, __FREE_STR ; Returns 0 if A == B + dec a ; Returns 0 if A == 1 => a$ > b$ + jp __FREE_STR +__STRLE: ; Compares a$ <= b$ (HL = ptr a$, DE = ptr b$). Returns FF or FE (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + dec a ; Returns 0 if A == 1 => a$ < b$ + jp __FREE_STR +__STRGT: ; Compares a$ > b$ (HL = ptr a$, DE = ptr b$). Returns 2 (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + or a + jp z, __FREE_STR ; Returns 0 if A == B + inc a ; Returns 0 if A == -1 => a$ < b$ + jp __FREE_STR +__STRGE: ; Compares a$ >= b$ (HL = ptr a$, DE = ptr b$). Returns 1 or 2 (True) or 0 (False) + push hl + push de + call __STRCMP + pop de + pop hl + inc a ; Returns 0 if A == -1 => a$ < b$ +__FREE_STR: ; This exit point will test A' for bits 0 and 1 + ; If bit 0 is 1 => Free memory from HL pointer + ; If bit 1 is 1 => Free memory from DE pointer + ; Finally recovers A, to return the result + PROC + LOCAL __FREE_STR2 + LOCAL __FREE_END + ;; normalize boolean + sub 1 + sbc a, a + inc a + ex af, af' + bit 0, a + jr z, __FREE_STR2 + push af + push de + call __MEM_FREE + pop de + pop af +__FREE_STR2: + bit 1, a + jr z, __FREE_END + ex de, hl + call __MEM_FREE +__FREE_END: + ex af, af' + ret + ENDP + pop namespace +#line 474 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/strslice.asm" + ; String slicing library + ; HL = Str pointer + ; DE = String start + ; BC = String character end + ; A register => 0 => the HL pointer wont' be freed from the HEAP + ; e.g. a$(5 TO 10) => HL = a$; DE = 5; BC = 10 + ; This implements a$(X to Y) being X and Y first and + ; last characters respectively. If X > Y, NULL is returned + ; Otherwise returns a pointer to a$ FROM X to Y (starting from 0) + ; if Y > len(a$), then a$ will be padded with spaces (reallocating + ; it in dynamic memory if needed). Returns pointer (HL) to resulting + ; string. NULL (0) if no memory for padding. + ; + push namespace core +__STRSLICE: ; Callee entry + pop hl ; Return ADDRESS + pop bc ; Last char pos + pop de ; 1st char pos + ex (sp), hl ; CALLEE. -> String start +__STRSLICE_FAST: ; __FASTCALL__ Entry + PROC + LOCAL __CONT + LOCAL __EMPTY + LOCAL __FREE_ON_EXIT + push hl ; Stores original HL pointer to be recovered on exit + ex af, af' ; Saves A register for later + push hl + call __STRLEN + inc bc ; Last character position + 1 (string starts from 0) + or a + sbc hl, bc ; Compares length with last char position + jr nc, __CONT ; If Carry => We must copy to end of string + add hl, bc ; Restore back original LEN(a$) in HL + ld b, h + ld c, l ; Copy to the end of str + ccf ; Clears Carry flag for next subtraction +__CONT: + ld h, b + ld l, c ; HL = Last char position to copy (1 for char 0, 2 for char 1, etc) + sbc hl, de ; HL = LEN(a$) - DE => Number of chars to copy + jr z, __EMPTY ; 0 Chars to copy => Return HL = 0 (NULL STR) + jr c, __EMPTY ; If Carry => Nothing to return (NULL STR) + ld b, h + ld c, l ; BC = Number of chars to copy + inc bc + inc bc ; +2 bytes for string length number + push bc + push de + call __MEM_ALLOC + pop de + pop bc + ld a, h + or l + jr z, __EMPTY ; Return if NULL (no memory) + dec bc + dec bc ; Number of chars to copy (Len of slice) + ld (hl), c + inc hl + ld (hl), b + inc hl ; Stores new string length + ex (sp), hl ; Pointer to A$ now in HL; Pointer to new string chars in Stack + inc hl + inc hl ; Skip string length + add hl, de ; Were to start from A$ + pop de ; Start of new string chars + push de ; Stores it again + ldir ; Copies BC chars + pop de + dec de + dec de ; Points to String LEN start + ex de, hl ; Returns it in HL + jr __FREE_ON_EXIT +__EMPTY: ; Return NULL (empty) string + pop hl + ld hl, 0 ; Return NULL +__FREE_ON_EXIT: + ex af, af' ; Recover original A register + ex (sp), hl ; Original HL pointer + or a + call nz, __MEM_FREE + pop hl ; Recover result + ret + ENDP + pop namespace +#line 476 "/zxbasic/src/lib/arch/zx48k/stdlib/spectranet.bas" +.LABEL.__LABEL38: + DEFB 00h + DEFB 00h + DEFB 02h + END diff --git a/tests/functional/arch/zx81sd/stdlib_spectranet.bas b/tests/functional/arch/zx81sd/stdlib_spectranet.bas new file mode 100644 index 000000000..8a52f0b32 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_spectranet.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/stdlib_winscroll.asm b/tests/functional/arch/zx81sd/stdlib_winscroll.asm new file mode 100644 index 000000000..1b0e80054 --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_winscroll.asm @@ -0,0 +1,2774 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_WinScrollRight: +#line 27 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AfterLDDR, AfterLDDR2, AfterLDDR3 + ld b, a + pop hl + pop de + ld a, d + pop de + add a, d + dec a + ld c, a + ex (sp), hl + ld e, h + ld a, e + or a + ret z + ld a, d + or a + ret z + sub 2 + inc a + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de +BucleRows: + push bc + push hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + push hl + jr c,AfterLDDR + ld d,h + ld e,l + dec hl + ld c,a + lddr + ex de,hl +AfterLDDR: + ld (hl),b + pop hl + inc h + dec ixH + jp nz,BucleScans + jr c,AfterLDDR2 + ld d,h + ld e,l + dec hl + ld c,a + lddr + ex de,hl +AfterLDDR2: + ld (hl),b + pop hl + jr c,AfterLDDR3 + ld d,h + ld e,l + dec hl + ld c,a + push de + lddr + pop hl +AfterLDDR3: + ex af,af' + ld de,32 + add hl,de + pop bc + inc b + dec ixL + jp nz,BucleRows + pop ix + ENDP + pop namespace +#line 139 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +_WinScrollRight__leave: + ret +_WinScrollLeft: +#line 149 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AfterLDIR, AfterLDIR2, AfterLDIR3 + ld b, a + pop hl + pop de + ld c, d + pop de + ex (sp), hl + ld e, h + ld a, e + or a + ret z + ld a, d + or a + ret z + sub 2 + inc a + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de +BucleRows: + push bc + push hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + push hl + jr c,AfterLDIR + ld d,h + ld e,l + inc hl + ld c,a + ldir + ex de,hl +AfterLDIR: + ld (hl),b + pop hl + inc h + dec ixH + jp nz,BucleScans + jr c,AfterLDIR2 + ld d,h + ld e,l + inc hl + ld c,a + ldir + ex de,hl +AfterLDIR2: + ld (hl),b + pop hl + jr c,AfterLDIR3 + ld d,h + ld e,l + inc hl + ld c,a + push de + ldir + pop hl +AfterLDIR3: + ex af,af' + ld de,32 + add hl,de + pop bc + inc b + dec ixL + jp nz,BucleRows + pop ix + ENDP + pop namespace +#line 258 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +_WinScrollLeft__leave: + ret +_WinScrollUp: +#line 268 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanBottomRow, CleanBottomScans, AfterLDIR + ld b, a + pop hl + pop de + ld c, d + pop de + ex (sp), hl + ld e, h + ld a, e + or a + ret z + ld a, d + or a + ret z + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de + ld (AttrAddress+1),hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + push hl +BucleRows: + dec ixL + jr z,CleanBottomRow + inc b + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + pop de + push hl + push bc + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + ld c,a + push de + push hl + ldir + pop hl + pop de + inc h + inc d + dec ixH + jp nz,BucleScans + ld c,a + ldir +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l + ld c,32 + add hl,bc + ld (AttrAddress+1),hl + ld c,a + ldir + ex af,af' + pop bc + jp BucleRows +CleanBottomRow: + ld b,0 + ex af,af' + ld ixH,8 + pop hl +CleanBottomScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanBottomScans + pop ix + ENDP + pop namespace +#line 402 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +_WinScrollUp__leave: + ret +_WinScrollDown: +#line 412 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" + push namespace core + PROC + LOCAL BucleRows, BucleScans, AttrAddress + LOCAL CleanTopRow, CleanTopScans, AfterLDIR + ld b, a + pop hl + pop de + ld c, d + pop de + ex (sp), hl + ld e, h + ld a, b + add a, e + dec a + ld b, a + ld a, e + or a + ret z + ld a, d + or a + ret z + ex af,af' + push ix + ld ixL,e + ld l,b + ld h,0 + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld a,l + add a,c + ld l,a + ld de,(SCREEN_ATTR_ADDR) + add hl,de + ld (AttrAddress+1),hl + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + push hl +BucleRows: + dec ixL + jr z,CleanTopRow + dec b + ld a,b + and %00011000 + ld h,a + ld a,b + and %00000111 + rrca + rrca + rrca + add a,c + ld l,a + ld de,(SCREEN_ADDR) + add hl,de + pop de + push hl + push bc + ld b,0 + ex af,af' + ld ixH,7 +BucleScans: + ld c,a + push de + push hl + ldir + pop hl + pop de + inc h + inc d + dec ixH + jp nz,BucleScans + ld c,a + ldir +AttrAddress: + ld hl,AttrAddress + ld d,h + ld e,l + ld c,32 + sbc hl,bc + ld (AttrAddress+1),hl + ld c,a + ldir + ex af,af' + pop bc + jp BucleRows +CleanTopRow: + ld b,0 + ex af,af' + ld ixH,8 + pop hl +CleanTopScans: + ld (hl),b + ld c,a + dec c + jr z,AfterLDIR + push hl + ld d,h + ld e,l + inc de + ldir + pop hl +AfterLDIR: + inc h + dec ixH + jp nz,CleanTopScans + pop ix + ENDP + pop namespace +#line 550 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" +_WinScrollDown__leave: + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 555 "/zxbasic/src/lib/arch/zx48k/stdlib/winscroll.bas" + END diff --git a/tests/functional/arch/zx81sd/stdlib_winscroll.bas b/tests/functional/arch/zx81sd/stdlib_winscroll.bas new file mode 100644 index 000000000..35234d39d --- /dev/null +++ b/tests/functional/arch/zx81sd/stdlib_winscroll.bas @@ -0,0 +1 @@ +#include diff --git a/tests/functional/arch/zx81sd/sub16.asm b/tests/functional/arch/zx81sd/sub16.asm new file mode 100644 index 000000000..a292add65 --- /dev/null +++ b/tests/functional/arch/zx81sd/sub16.asm @@ -0,0 +1,96 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/sub16.bas b/tests/functional/arch/zx81sd/sub16.bas new file mode 100644 index 000000000..547ef96f7 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/sub16a.asm b/tests/functional/arch/zx81sd/sub16a.asm new file mode 100644 index 000000000..4868edacf --- /dev/null +++ b/tests/functional/arch/zx81sd/sub16a.asm @@ -0,0 +1,82 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/sub16a.bas b/tests/functional/arch/zx81sd/sub16a.bas new file mode 100644 index 000000000..5958f41ab --- /dev/null +++ b/tests/functional/arch/zx81sd/sub16a.bas @@ -0,0 +1,4 @@ + +REM another SUB16 tests +DIM a As Uinteger +a = a - a - a diff --git a/tests/functional/arch/zx81sd/sub16b.asm b/tests/functional/arch/zx81sd/sub16b.asm new file mode 100644 index 000000000..1bf2e0e7d --- /dev/null +++ b/tests/functional/arch/zx81sd/sub16b.asm @@ -0,0 +1,90 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/sub16b.bas b/tests/functional/arch/zx81sd/sub16b.bas new file mode 100644 index 000000000..0fcad1bea --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/sub8.asm b/tests/functional/arch/zx81sd/sub8.asm new file mode 100644 index 000000000..1e23acf40 --- /dev/null +++ b/tests/functional/arch/zx81sd/sub8.asm @@ -0,0 +1,92 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/sub8.bas b/tests/functional/arch/zx81sd/sub8.bas new file mode 100644 index 000000000..3a1c98f00 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/sub8a.asm b/tests/functional/arch/zx81sd/sub8a.asm new file mode 100644 index 000000000..b871a03c7 --- /dev/null +++ b/tests/functional/arch/zx81sd/sub8a.asm @@ -0,0 +1,80 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/sub8a.bas b/tests/functional/arch/zx81sd/sub8a.bas new file mode 100644 index 000000000..f0e9cc8bb --- /dev/null +++ b/tests/functional/arch/zx81sd/sub8a.bas @@ -0,0 +1,4 @@ + +REM another ADD8 tests +DIM a As Byte +a = a - a - a diff --git a/tests/functional/arch/zx81sd/sub8b.asm b/tests/functional/arch/zx81sd/sub8b.asm new file mode 100644 index 000000000..c46de9b92 --- /dev/null +++ b/tests/functional/arch/zx81sd/sub8b.asm @@ -0,0 +1,86 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/sub8b.bas b/tests/functional/arch/zx81sd/sub8b.bas new file mode 100644 index 000000000..895fb267d --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/subf00.asm b/tests/functional/arch/zx81sd/subf00.asm new file mode 100644 index 000000000..462787c2a --- /dev/null +++ b/tests/functional/arch/zx81sd/subf00.asm @@ -0,0 +1,2422 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + 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 17 "arch/zx81sd/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 18 "arch/zx81sd/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 19 "arch/zx81sd/subf00.bas" + END diff --git a/tests/functional/arch/zx81sd/subf00.bas b/tests/functional/arch/zx81sd/subf00.bas new file mode 100644 index 000000000..f76db1231 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/subf01.asm b/tests/functional/arch/zx81sd/subf01.asm new file mode 100644 index 000000000..0ff413c38 --- /dev/null +++ b/tests/functional/arch/zx81sd/subf01.asm @@ -0,0 +1,2398 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/arith/subf.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 40 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 41 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 12 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + 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 21 "arch/zx81sd/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 22 "arch/zx81sd/subf01.bas" + END diff --git a/tests/functional/arch/zx81sd/subf01.bas b/tests/functional/arch/zx81sd/subf01.bas new file mode 100644 index 000000000..49f56f359 --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/subf16c.asm b/tests/functional/arch/zx81sd/subf16c.asm new file mode 100644 index 000000000..a3344969e --- /dev/null +++ b/tests/functional/arch/zx81sd/subf16c.asm @@ -0,0 +1,157 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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 37 "arch/zx81sd/subf16c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 38 "arch/zx81sd/subf16c.bas" + END diff --git a/tests/functional/arch/zx81sd/subf16c.bas b/tests/functional/arch/zx81sd/subf16c.bas new file mode 100644 index 000000000..9e24837ad --- /dev/null +++ b/tests/functional/arch/zx81sd/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/zx81sd/subi32c.asm b/tests/functional/arch/zx81sd/subi32c.asm new file mode 100644 index 000000000..b58141956 --- /dev/null +++ b/tests/functional/arch/zx81sd/subi32c.asm @@ -0,0 +1,166 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 + halt + ;; --- end of user code --- +#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 46 "arch/zx81sd/subi32c.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/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 47 "arch/zx81sd/subi32c.bas" + END diff --git a/tests/functional/arch/zx81sd/subi32c.bas b/tests/functional/arch/zx81sd/subi32c.bas new file mode 100644 index 000000000..73566b4c7 --- /dev/null +++ b/tests/functional/arch/zx81sd/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 diff --git a/tests/functional/arch/zx81sd/ubound0.asm b/tests/functional/arch/zx81sd/ubound0.asm new file mode 100644 index 000000000..bf18d4ecf --- /dev/null +++ b/tests/functional/arch/zx81sd/ubound0.asm @@ -0,0 +1,96 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + jp .core.__MAIN_PROGRAM__ +.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 +.LABEL.__LABEL0: + DEFW 0001h + DEFW 0003h + DEFB 01h +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld hl, 2 + ld (_b), hl + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt + ;; --- end of user code --- + END diff --git a/tests/functional/arch/zx81sd/ubound0.bas b/tests/functional/arch/zx81sd/ubound0.bas new file mode 100644 index 000000000..b28466db3 --- /dev/null +++ b/tests/functional/arch/zx81sd/ubound0.bas @@ -0,0 +1,4 @@ + +DIM a(3 TO 5, 7 TO 9) As UByte + +LET b = UBound(a) diff --git a/tests/functional/arch/zx81sd/ubound12.asm b/tests/functional/arch/zx81sd/ubound12.asm new file mode 100644 index 000000000..2dbef0be5 --- /dev/null +++ b/tests/functional/arch/zx81sd/ubound12.asm @@ -0,0 +1,3154 @@ + org $0000 + jp $0100 + org $0008 + di + halt + org $0010 + di + halt + org $0018 + di + halt + org $0020 + di + halt + org $0028 + jp .core.FP_CALC_ENTRY + org $0030 + di + halt + org $0038 + push af +.core.__RST38_WAIT: + in a, ($AF) + and $7E + jr z, .core.__RST38_WAIT + pop af + ret + org $0066 + retn + org 256 +.core.__START_PROGRAM: + ld b, 9 + ld a, 1 + ld c, 0xe7 + out (c), a + ld b, 10 + ld a, 2 + ld c, 0xe7 + out (c), a + ld b, 11 + ld a, 3 + ld c, 0xe7 + out (c), a + ld b, 12 + ld a, 4 + ld c, 0xe7 + out (c), a + ld b, 13 + ld a, 5 + ld c, 0xe7 + out (c), a + ld b, 15 + ld a, 7 + ld c, 0xe7 + out (c), a + ld sp, 0x7fff + call .core.SD81_INIT_SYSVARS + call .core.__MEM_INIT + jp .core.__MAIN_PROGRAM__ +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 16127 + ; Defines HEAP ADDRESS +.core.ZXBASIC_MEM_HEAP EQU 33024 + ; 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 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test1 + ld hl, 0 + ld b, h + ld c, l +.core.__END_PROGRAM: + di + halt +_test3: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + ld (_b), hl + jp .LABEL.__LABEL0 +.LABEL.__LABEL3: + ld hl, (_b) + push hl + ld l, (ix+4) + ld h, (ix+5) + call .core.__UBOUND + ld (_c), hl +.LABEL.__LABEL4: + ld hl, (_b) + inc hl + ld (_b), hl +.LABEL.__LABEL0: + ld hl, 3 + ld de, (_b) + or a + sbc hl, de + jp nc, .LABEL.__LABEL3 +.LABEL.__LABEL2: +_test3__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret +_test1: + push ix + ld ix, 0 + add ix, sp + ld hl, -8 + add hl, sp + ld sp, hl + ld (hl), 0 + ld bc, 7 + ld d, h + ld e, l + inc de + ldir + ld hl, _test1.a1.__UBOUND__ + push hl + ld hl, 0 + push hl + ld hl, -8 + ld de, .LABEL.__LABEL5 + ld bc, 18 + call .core.__ALLOC_LOCAL_ARRAY_WITH_BOUNDS + push ix + pop hl + ld de, -8 + add hl, de + push hl + call _test2 +_test1__leave: + ex af, af' + exx + ld hl, 9 + push hl + ld l, (ix-6) + ld h, (ix-5) + call .core.__ARRAYSTR_FREE_MEM + ex af, af' + exx + ld sp, ix + pop ix + ret +_test1.a1.__UBOUND__: + DEFW 0005h + DEFW 0009h +_test2: + push ix + ld ix, 0 + add ix, sp + ld l, (ix+4) + ld h, (ix+5) + push hl + call _test3 +_test2__leave: + ld sp, ix + pop ix + exx + pop hl + ex (sp), hl + exx + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.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 +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx81sd/runtime/error.asm" + ; Simple error control routines — ZX81 + SD81 Booster + ; Sustituye RST 8 (error handler de la ROM Spectrum) por una parada +; limpia: guarda el código de error en ERR_NR y detiene la CPU. + ; Las interrupciones ya están desactivadas (DI desde el bootstrap). +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + ; ----------------------------------------------------------------------- + ; ZX81 + SD81 Booster System Variables + ; +; SCREEN_ADDR / SCREEN_ATTR_ADDR: bloque 6 ($C000), pantalla Spectrum + ; emulada por la FPGA del SD81 Booster en modo Superfast HiRes Spectrum. + ; + ; Las variables dinámicas del runtime se sitúan en $8000+ (bloques 4-5), + ; fuera de la zona de código ejecutable ($0000-$7FFF), para no partir + ; el espacio de ejecución del usuario. + ; ----------------------------------------------------------------------- + ; Estos ficheros se incluyen siempre a través de sysvars.asm (primer fichero + ; en incluirse en cualquier programa zx81sd) para que sus #init se registren + ; en la primera pasada del preprocesador, antes de que emit_prologue() genere + ; las llamadas CALL a las rutinas de inicialización. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + ; BOOTSTRAP — Inicialización de las sysvars del runtime (stage 2, parte ASM) + ; + ; El hardware (paginación bloques 1-5, SP, DI) es inicializado directamente + ; por el prólogo del backend Python (emit_prologue), que emite esas + ; instrucciones como constantes de arquitectura conocidas en tiempo de + ; compilación. + ; + ; Esta rutina se ocupa de los valores por defecto de las sysvars en $8000+. + ; Se registra con #init para que el compilador inserte automáticamente + ; CALL SD81_INIT_SYSVARS en el prólogo, justo antes del salto al programa. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/charset.asm" + ; CHARSET — Fuente de caracteres 8x8 compatible Spectrum + ; + ; 96 caracteres × 8 bytes, desde CHR$(32) hasta CHR$(127). +; Fichero externo: specfont.bin (debe estar en el mismo directorio). + push namespace core +__ZX81SD_CHARSET: + INCBIN "specfont.bin" + ; Area de UDGs (CHR$(144) a CHR$(164), 21 caracteres como en el Spectrum). +; La fuente solo cubre CHR$(32)-CHR$(127) (768 bytes): sin este bloque, + ; apuntar UDG a "font+896" caia 128 bytes MAS ALLA del final de la fuente, + ; sobre el codigo que tocara despues en el enlazado (los POKE USR CHR$ + ; de un programa corrompian el runtime). SD81_INIT_SYSVARS la inicializa + ; con copias de las letras A-U, igual que la ROM del Spectrum. +__ZX81SD_UDG_AREA: + defs 21 * 8 + pop namespace +#line 14 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; el vector RST $28h (src/arch/zx81sd/backend/main.py, emit_prologue) salta + ; incondicionalmente a FP_CALC_ENTRY, así que esa etiqueta debe existir en + ; todo binario, aunque el programa concreto no acabe generando código que + ; la invoque. +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" + ; =========================================================================== + ; fp_calc.asm — Calculador de coma flotante (RST $28h) para ZX81 + SD81 Booster + ; + ; Port del calculador de la ROM del ZX Spectrum 48K (CALCULATE, $335B, y las + ; rutinas de las que depende) a código propio en RAM, ya que en zx81sd no hay + ; ninguna ROM mapeada en tiempo de ejecucion (el bloque 0 lo ocupa entero + ; nuestro binario compilado). + ; + ; Gracias a este port, todo el runtime de coma flotante de zx48k (addf.asm, + ; subf.asm, mulf.asm, divf.asm, negf.asm, cmp/*.asm, bool/*.asm, math/*.asm, + ; str.asm, printf.asm, val.asm...) funciona TAL CUAL, sin modificar ni un +; solo fichero: todos ellos hacen "rst 28h" seguido de bytes de "literal" de + ; operacion calculadora, y ahora $0028 contiene codigo real (ver + ; src/arch/zx81sd/backend/main.py, emit_prologue) en vez de un DI/HALT. + ; +; FASE 1 (este fichero): motor CALCULATE + pila de numeros FP + aritmetica + ; (suma/resta/multiplicacion/division) + comparaciones numericas + booleanas + ; (AND/OR/NOT) + funciones unarias basicas (ABS/NEGATE/SGN/INT/truncate). +; Pendiente en fases posteriores: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR (usan el + ; "series generator" ya portado aqui, mas la tabla de coeficientes de cada + ; funcion, que aun no esta), STR$ (conversion numero->texto) y VAL (parseo de +; texto->numero, con semantica simplificada: solo literales decimales, no + ; evaluacion de expresiones completas como hace la ROM real — ver conversacion + ; de diseño). + ; +; Formato de los numeros (5 bytes), identico al de la ROM Spectrum: +; Entero pequeño: byte1=$00, byte2=signo($00/$FF), byte3=lo, byte4=hi, byte5=$00 +; Coma flotante: byte1=exponente sesgado(+$80), byte2..5=mantisa de 32 bits + ; con el bit 7 del byte2 usado como signo (bit de mantisa + ; implicito, siempre a 1 salvo cuando se usa para el signo) + ; +; Referencia: disassembly comentado de la ROM del ZX Spectrum 48K +; (C:\ClaudeCode\ZXBASIC-SD81\Spectrum48.asm), seccion "FLOATING-POINT + ; CALCULATOR". Las etiquetas Lxxxx conservan la direccion original de la ROM +; (solo como identificador legible, no como direccion real: aqui son + ; reubicables) para facilitar la referencia cruzada con el disassembly. + ; =========================================================================== +#line 1 "/zxbasic/src/lib/arch/zx81sd/runtime/stackf.asm" + ; stackf.asm (zx81sd) — Gestión de la pila del calculador FP + ; + ; Sustituye a zx48k/runtime/stackf.asm, que define __FPSTACK_PUSH/POP como + ; direcciones FIJAS de la ROM del Spectrum ($2AB6h STK-STORE, $2BF1h + ; STK-FETCH). En zx81sd esas direcciones son parte de nuestro propio + ; binario compilado (varían de un programa a otro), así que no se pueden + ; usar como constantes — hay que reimplementar ambas rutinas como código + ; reubicable normal, usando fp_calc.asm (mismo formato de pila y de número + ; de 5 bytes que el motor CALCULATE ya portado). + push namespace core + ; --------------------------------------------------------------------------- + ; __FPSTACK_PUSH — Apila los registros A,E,D,C,B (5 bytes) en la pila FP + ; Sustituye a STK-STORE ($2AB6h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_PUSH: + push bc + push af + ld bc, 5 + call CALC_TEST_ROOM + pop af + pop bc + ld hl, (FP_STKEND) + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + inc hl + ld (FP_STKEND), hl + ret +__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 + ; --------------------------------------------------------------------------- + ; __FPSTACK_POP — Extrae los últimos 5 bytes de la pila FP a A,E,D,C,B + ; Sustituye a STK-FETCH ($2BF1h ROM Spectrum) + ; --------------------------------------------------------------------------- +__FPSTACK_POP: + ld hl, (FP_STKEND) + dec hl + ld b, (hl) + dec hl + ld c, (hl) + dec hl + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld a, (hl) + ld (FP_STKEND), hl + ret + pop namespace +#line 42 "/zxbasic/src/lib/arch/zx81sd/runtime/fp_calc.asm" +; fp_calc.asm se incluye siempre (no solo cuando el programa usa FLOAT): + ; ver src/arch/zx81sd/backend/main.py. Por eso debe bastarse a si mismo e + ; incluir aqui stackf.asm (de donde vienen __FPSTACK_PUSH/__FPSTACK_POP), + ; en vez de depender de que el fichero que lo incluya lo haga tambien. + push namespace core + ; --------------------------------------------------------------------------- + ; Punto de entrada — sustituye a RST $28h + ; --------------------------------------------------------------------------- +FP_CALC_ENTRY: + jp L335B + ; --------------------------------------------------------------------------- + ; TEST-ROOM propio — sustituye a TEST-ROOM ($1F05, que comprobaba espacio + ; libre contra el puntero de pila SP). Aqui la pila de numeros FP es un + ; buffer fijo (FP_CALC_STACK..FP_CALC_STACK_END en sysvars.asm), asi que + ; basta comprobar que FP_STKEND + BC no se sale del buffer. +; Entrada: BC = bytes requeridos. Sale con BC intacto si hay espacio. + ; --------------------------------------------------------------------------- +CALC_TEST_ROOM: + push hl + push de + ld hl, (FP_STKEND) + add hl, bc + ld de, FP_CALC_STACK_END + or a + sbc hl, de + pop de + pop hl + jr c, CALC_TEST_ROOM_OK + ld a, ERROR_OutOfMemory + jp __ERROR +CALC_TEST_ROOM_OK: + ret + ; --------------------------------------------------------------------------- + ; THE 'TEST FIVE SPACES' SUBROUTINE ($33A9 TEST-5-SP) + ; --------------------------------------------------------------------------- +L33A9: + push de + push hl + ld bc, 5 + call CALC_TEST_ROOM + pop hl + pop de + ret + ; --------------------------------------------------------------------------- + ; STACK-NUM ($33B4) — apila un numero de 5 bytes apuntado por HL + ; --------------------------------------------------------------------------- +L33B4: + ld de, (FP_STKEND) + call L33C0 + ld (FP_STKEND), de + ret + ; --------------------------------------------------------------------------- + ; MOVE-FP / duplicate (literal $31, $33C0) + ; --------------------------------------------------------------------------- +L33C0: + call L33A9 + ldir + ret + ; --------------------------------------------------------------------------- + ; stk-data (literal $34, $33C6) / STK-CONST ($33C8) / STK-ZEROS ($33F1) + ; --------------------------------------------------------------------------- +L33C6: + ld h, d + ld l, e +L33C8: + call L33A9 + exx + push hl + exx + ex (sp), hl + push bc + ld a, (hl) + and $C0 + rlca + rlca + ld c, a + inc c + ld a, (hl) + and $3F + jr nz, L33DE + inc hl + ld a, (hl) +L33DE: + add a, $50 + ld (de), a + ld a, 5 + sub c + inc hl + inc de + ld b, 0 + ldir + pop bc + ex (sp), hl + exx + pop hl + exx + ld b, a + xor a +L33F1: + dec b + ret z + ld (de), a + inc de + jr L33F1 + ; --------------------------------------------------------------------------- + ; SKIP-CONS ($33F7 / $33F8) + ; --------------------------------------------------------------------------- +L33F7: + and a +L33F8: + ret z + push af + push de + ld de, 0 + call L33C8 + pop de + pop af + dec a + jr L33F8 + ; --------------------------------------------------------------------------- + ; LOC-MEM ($3406) + ; --------------------------------------------------------------------------- +L3406: + ld c, a + rlca + rlca + add a, c + ld c, a + ld b, 0 + add hl, bc + ret + ; --------------------------------------------------------------------------- + ; get-mem-xx (literales $E0-$FF, $340F) + ; --------------------------------------------------------------------------- +L340F: + push de + ld hl, (FP_MEM) + call L3406 + call L33C0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; stk-const-xx (literales $A0-$BF, $341B) + tabla de constantes + ; --------------------------------------------------------------------------- +L341B: + ld h, d + ld l, e + exx + push hl + ld hl, L32C5 + exx + call L33F7 + call L33C8 + exx + pop hl + exx + ret + ; --------------------------------------------------------------------------- + ; st-mem-xx (literales $C0-$DF, $342D) + ; --------------------------------------------------------------------------- +L342D: + push hl + ex de, hl + ld hl, (FP_MEM) + call L3406 + ex de, hl + call L33C0 + ex de, hl + pop hl + ret + ; --------------------------------------------------------------------------- + ; exchange (literal $01, $343C) + ; --------------------------------------------------------------------------- +L343C: + ld b, 5 +L343E: + ld a, (de) + ld c, (hl) + ex de, hl + ld (de), a + ld (hl), c + inc hl + inc de + djnz L343E + ex de, hl + ret + ; --------------------------------------------------------------------------- + ; series generator (literales $80-$9F, $3449) — necesario para SIN/COS/EXP/LN + ; (aun sin las tablas de coeficientes; se añadirán en una fase posterior) + ; --------------------------------------------------------------------------- +L3449: + ld b, a + call L335E + defb $31 ;;duplicate x,x + defb $0F ;;addition x+x + defb $C0 ;;st-mem-0 x+x + defb $02 ;;delete . + defb $A0 ;;stk-zero 0 + defb $C2 ;;st-mem-2 0 +L3453: + defb $31 ;;duplicate v,v. + defb $E0 ;;get-mem-0 v,v,x+2 + defb $04 ;;multiply v,v*x+2 + defb $E2 ;;get-mem-2 v,v*x+2,v + defb $C1 ;;st-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + call L33C6 + call L3362 + defb $0F ;;addition + defb $01 ;;exchange + defb $C2 ;;st-mem-2 + defb $02 ;;delete + defb $35 ;;dec-jr-nz + defb (L3453 - $) & 0FFh ;;back to G-LOOP + defb $E1 ;;get-mem-1 + defb $03 ;;subtract + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; abs (literal $2A, $346A) / negate (literal $1B, $346E) / sgn (literal $29, + ; $3492) + ; --------------------------------------------------------------------------- +L346A: + ld b, $FF + jr L3474 +L346E: + call L34E9 + ret c + ld b, 0 +L3474: + ld a, (hl) + and a + jr z, L3483 + inc hl + ld a, b + and $80 + or (hl) + rla + ccf + rra + ld (hl), a + dec hl + ret +L3483: + push de + push hl + call L2D7F + pop hl + ld a, b + or c + cpl + ld c, a + call L2D8E + pop de + ret +L3492: + call L34E9 + ret c + push de + ld de, 1 + inc hl + rl (hl) + dec hl + sbc a, a + ld c, a + call L2D8E + pop de + ret + ; --------------------------------------------------------------------------- + ; INT-FETCH ($2D7F) / INT-STORE ($2D8E) + ; --------------------------------------------------------------------------- +L2D7F: + inc hl + ld c, (hl) + inc hl + ld a, (hl) + xor c + sub c + ld e, a + inc hl + ld a, (hl) + adc a, c + xor c + ld d, a + ret +L2D8E: + push hl + ld (hl), 0 + inc hl + ld (hl), c + inc hl + ld a, e + xor c + sub c + ld (hl), a + inc hl + ld a, d + adc a, c + xor c + ld (hl), a + inc hl + ld (hl), 0 + pop hl + ret + ; --------------------------------------------------------------------------- + ; PREP-ADD ($2F9B) + ; --------------------------------------------------------------------------- +L2F9B: + ld a, (hl) + ld (hl), 0 + and a + ret z + inc hl + bit 7, (hl) + set 7, (hl) + dec hl + ret z + push bc + ld bc, 5 + add hl, bc + ld b, c + ld c, a + scf +L2FAF: + dec hl + ld a, (hl) + cpl + adc a, 0 + ld (hl), a + djnz L2FAF + ld a, c + pop bc + ret + ; --------------------------------------------------------------------------- + ; FETCH-TWO ($2FBA) + ; --------------------------------------------------------------------------- +L2FBA: + push hl + push af + ld c, (hl) + inc hl + ld b, (hl) + ld (hl), a + inc hl + ld a, c + ld c, (hl) + push bc + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ex de, hl + ld d, a + ld e, (hl) + push de + inc hl + ld d, (hl) + inc hl + ld e, (hl) + push de + exx + pop de + pop hl + pop bc + exx + inc hl + ld d, (hl) + inc hl + ld e, (hl) + pop af + pop hl + ret + ; --------------------------------------------------------------------------- + ; SHIFT-FP ($2FDD) / ADD-BACK ($3004) + ; --------------------------------------------------------------------------- +L2FDD: + and a + ret z + cp $21 + jr nc, L2FF9 + push bc + ld b, a +L2FE5: + exx + sra l + rr d + rr e + exx + rr d + rr e + djnz L2FE5 + pop bc + ret nc + call L3004 + ret nz +L2FF9: + exx + xor a +L2FFB: + ld l, 0 + ld d, a + ld e, l + exx + ld de, 0 + ret +L3004: + inc e + ret nz + inc d + ret nz + exx + inc e + jr nz, L300D + inc d +L300D: + exx + ret + ; --------------------------------------------------------------------------- + ; subtract (literal $03, $300F) / addition (literal $0F, $3014) + ; --------------------------------------------------------------------------- +L300F: + ex de, hl + call L346E + ex de, hl +L3014: + ld a, (de) + or (hl) + jr nz, L303E + push de + inc hl + push hl + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + inc hl + inc hl + ld a, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + pop hl + ex de, hl + add hl, bc + ex de, hl + adc a, (hl) + rrca + adc a, 0 + jr nz, L303C + sbc a, a + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + dec hl + dec hl + dec hl + pop de + ret +L303C: + dec hl + pop de +L303E: + call L3293 + exx + push hl + exx + push de + push hl + call L2F9B + ld b, a + ex de, hl + call L2F9B + ld c, a + cp b + jr nc, L3055 + ld a, b + ld b, c + ex de, hl +L3055: + push af + sub b + call L2FBA + call L2FDD + pop af + pop hl + ld (hl), a + push hl + ld l, b + ld h, c + add hl, de + exx + ex de, hl + adc hl, bc + ex de, hl + ld a, h + adc a, l + ld l, a + rra + xor l + exx + ex de, hl + pop hl + rra + jr nc, L307C + ld a, 1 + call L2FDD + inc (hl) + jr z, L309F +L307C: + exx + ld a, l + and $80 + exx + inc hl + ld (hl), a + dec hl + jr z, L30A5 + ld a, e + neg + ccf + ld e, a + ld a, d + cpl + adc a, 0 + ld d, a + exx + ld a, e + cpl + adc a, 0 + ld e, a + ld a, d + cpl + adc a, 0 + jr nc, L30A3 + rra + exx + inc (hl) +L309F: + jp z, L31AD + exx +L30A3: + ld d, a + exx +L30A5: + xor a + jp L3155 + ; --------------------------------------------------------------------------- + ; HL-HL*DE ($30A9) / PREP-M/D ($30C0) + ; --------------------------------------------------------------------------- +L30A9: + push bc + ld b, 16 + ld a, h + ld c, l + ld hl, 0 +L30B1: + add hl, hl + jr c, L30BE + rl c + rla + jr nc, L30BC + add hl, de + jr c, L30BE +L30BC: + djnz L30B1 +L30BE: + pop bc + ret +L30C0: + call L34E9 + ret c + inc hl + xor (hl) + set 7, (hl) + dec hl + ret + ; --------------------------------------------------------------------------- + ; multiply (literal $04, $30CA) + ; --------------------------------------------------------------------------- +L30CA: + ld a, (de) + or (hl) + jr nz, L30F0 + push de + push hl + push de + call L2D7F + ex de, hl + ex (sp), hl + ld b, c + call L2D7F + ld a, b + xor c + ld c, a + pop hl + call L30A9 + ex de, hl + pop hl + jr c, L30EF + ld a, d + or e + jr nz, L30EA + ld c, a +L30EA: + call L2D8E + pop de + ret +L30EF: + pop de +L30F0: + call L3293 + xor a + call L30C0 + ret c + exx + push hl + exx + push de + ex de, hl + call L30C0 + ex de, hl + jr c, L315D + push hl + call L2FBA + ld a, b + and a + sbc hl, hl + exx + push hl + sbc hl, hl + exx + ld b, $21 + jr L3125 +L3114: + jr nc, L311B + add hl, de + exx + adc hl, de + exx +L311B: + exx + rr h + rr l + exx + rr h + rr l +L3125: + exx + rr b + rr c + exx + rr c + rra + djnz L3114 + ex de, hl + exx + ex de, hl + exx + pop bc + pop hl + ld a, b + add a, c + jr nz, L313B + and a +L313B: + dec a + ccf +L313D: + rla + ccf + rra + jp p, L3146 + jr nc, L31AD + and a +L3146: + inc a + jr nz, L3151 + jr c, L3151 + exx + bit 7, d + exx + jr nz, L31AD +L3151: + ld (hl), a + exx + ld a, b + exx +L3155: + jr nc, L316C + ld a, (hl) + and a +L3159: + ld a, $80 + jr z, L315E +L315D: + xor a +L315E: + exx + and d + call L2FFB + rlca + ld (hl), a + jr c, L3195 + inc hl + ld (hl), a + dec hl + jr L3195 +L316C: + ld b, $20 +L316E: + exx + bit 7, d + exx + jr nz, L3186 + rlca + rl e + rl d + exx + rl e + rl d + exx + dec (hl) + jr z, L3159 + djnz L316E + jr L315D +L3186: + rla + jr nc, L3195 + call L3004 + jr nz, L3195 + exx + ld d, $80 + exx + inc (hl) + jr z, L31AD +L3195: + push hl + inc hl + exx + push de + exx + pop bc + ld a, b + rla + rl (hl) + rra + ld (hl), a + inc hl + ld (hl), c + inc hl + ld (hl), d + inc hl + ld (hl), e + pop hl + pop de + exx + pop hl + exx + ret +L31AD: + ld a, ERROR_NumberTooBig + jp __ERROR + ; --------------------------------------------------------------------------- + ; division (literal $05, $31AF) + ; --------------------------------------------------------------------------- +L31AF: + call L3293 + ex de, hl + xor a + call L30C0 + jr c, L31AD + ex de, hl + call L30C0 + ret c + exx + push hl + exx + push de + push hl + call L2FBA + exx + push hl + ld h, b + ld l, c + exx + ld h, c + ld l, b + xor a + ld b, $DF + jr L31E2 +L31D2: + rla + rl c + exx + rl c + rl b + exx +L31DB: + add hl, hl + exx + adc hl, hl + exx + jr c, L31F2 +L31E2: + sbc hl, de + exx + sbc hl, de + exx + jr nc, L31F9 + add hl, de + exx + adc hl, de + exx + and a + jr L31FA +L31F2: + and a + sbc hl, de + exx + sbc hl, de + exx +L31F9: + scf +L31FA: + inc b + jp m, L31D2 + push af + jr z, L31E2 + ld e, a + ld d, c + exx + ld e, c + ld d, b + pop af + rr b + pop af + rr b + exx + pop bc + pop hl + ld a, b + sub c + jp L313D + ; --------------------------------------------------------------------------- + ; Integer truncation towards zero (literal $3A, $3214) + ; --------------------------------------------------------------------------- +L3214: + ld a, (hl) + and a + ret z + cp $81 + jr nc, L3221 + ld (hl), 0 + ld a, $20 + jr L3272 +L3221: + cp $91 + jr nz, L323F + inc hl + inc hl + inc hl + ld a, $80 + and (hl) + dec hl + or (hl) + dec hl + jr nz, L3233 + ld a, $80 + xor (hl) +L3233: + dec hl + jr nz, L326C + ld (hl), a + inc hl + ld (hl), $FF + dec hl + ld a, $18 + jr L3272 +L323F: + jr nc, L326D + push de + cpl + add a, $91 + inc hl + ld d, (hl) + inc hl + ld e, (hl) + dec hl + dec hl + ld c, 0 + bit 7, d + jr z, L3252 + dec c +L3252: + set 7, d + ld b, 8 + sub b + add a, b + jr c, L325E + ld e, d + ld d, 0 + sub b +L325E: + jr z, L3267 + ld b, a +L3261: + srl d + rr e + djnz L3261 +L3267: + call L2D8E + pop de + ret +L326C: + ld a, (hl) +L326D: + sub $A0 + ret p + neg +L3272: + push de + ex de, hl + dec hl + ld b, a + srl b + srl b + srl b + jr z, L3283 +L327E: + ld (hl), 0 + dec hl + djnz L327E +L3283: + and $07 + jr z, L3290 + ld b, a + ld a, $FF +L328A: + sla a + djnz L328A + and (hl) + ld (hl), a +L3290: + ex de, hl + pop de + ret + ; --------------------------------------------------------------------------- + ; RE-ST-TWO ($3293) / RESTK-SUB ($3296) / re-stack (literal $3D, $3297) + ; --------------------------------------------------------------------------- +L3293: + call L3296 +L3296: + ex de, hl +L3297: + ld a, (hl) + and a + ret nz + push de + call L2D7F + xor a + inc hl + ld (hl), a + dec hl + ld (hl), a + ld b, $91 + ld a, d + and a + jr nz, L32B1 + or e + ld b, d + jr z, L32BD + ld d, e + ld e, b + ld b, $89 +L32B1: + ex de, hl +L32B2: + dec b + add hl, hl + jr nc, L32B2 + rrc c + rr h + rr l + ex de, hl +L32BD: + dec hl + ld (hl), e + dec hl + ld (hl), d + dec hl + ld (hl), b + pop de + ret + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF CONSTANTS' ($32C5-$32D6) + ; --------------------------------------------------------------------------- +L32C5: ;;stk-zero + defb $00, $B0, $00 +L32C8: ;;stk-one + defb $40, $B0, $00, $01 +L32CC: ;;stk-half + defb $30, $00 +L32CE: ;;stk-pi/2 + defb $F1, $49, $0F, $DA, $A2 +L32D3: ;;stk-ten + defb $40, $B0, $00, $0A + ; --------------------------------------------------------------------------- + ; THE 'TABLE OF ADDRESSES' ($32D7) — tbl-addrs + ; + ; Las entradas para funciones no soportadas (cadenas, USR, PEEK, IN, CODE, + ; LEN, READ-IN, VAL$) apuntan a CALC_UNSUPPORTED, que detiene la ejecucion + ; con un error claro si alguna vez se generase ese literal (no deberia +; ocurrir: el compilador de ZX BASIC no emite esos literales para nuestro + ; runtime, ver la lista de literales usados en arith/cmp/bool/math/*.asm). + ; --------------------------------------------------------------------------- +L32D7: + defw L368F ; $00 jump-true + defw L343C ; $01 exchange + defw L33A1 ; $02 delete + defw L300F ; $03 subtract + defw L30CA ; $04 multiply + defw L31AF ; $05 division + defw L3851 ; $06 to-power + defw L351B ; $07 or + defw L3524 ; $08 no-&-no + defw L353B ; $09 no-l-eql + defw L353B ; $0A no-gr-eql + defw L353B ; $0B nos-neql + defw L353B ; $0C no-grtr + defw L353B ; $0D no-less + defw L353B ; $0E nos-eql + defw L3014 ; $0F addition + defw CALC_UNSUPPORTED ; $10 str-&-no + defw CALC_UNSUPPORTED ; $11 str-l-eql + defw CALC_UNSUPPORTED ; $12 str-gr-eql + defw CALC_UNSUPPORTED ; $13 strs-neql + defw CALC_UNSUPPORTED ; $14 str-grtr + defw CALC_UNSUPPORTED ; $15 str-less + defw CALC_UNSUPPORTED ; $16 strs-eql + defw CALC_UNSUPPORTED ; $17 strs-add + defw CALC_UNSUPPORTED ; $18 val$ + defw CALC_UNSUPPORTED ; $19 usr-$ + defw CALC_UNSUPPORTED ; $1A read-in + defw L346E ; $1B negate + defw CALC_UNSUPPORTED ; $1C code + defw CALC_UNSUPPORTED ; $1D val (pendiente: parseo numerico propio) + defw CALC_UNSUPPORTED ; $1E len + defw L37B5 ; $1F sin + defw L37AA ; $20 cos + defw L37DA ; $21 tan + defw L3833 ; $22 asn + defw L3843 ; $23 acs + defw L37E2 ; $24 atn + defw L3713 ; $25 ln + defw L36C4 ; $26 exp + defw L36AF ; $27 int + defw L384A ; $28 sqr + defw L3492 ; $29 sgn + defw L346A ; $2A abs + defw CALC_UNSUPPORTED ; $2B peek + defw CALC_UNSUPPORTED ; $2C in + defw CALC_UNSUPPORTED ; $2D usr-no + defw CALC_UNSUPPORTED ; $2E str$ (pendiente) + defw CALC_UNSUPPORTED ; $2F chr$ + defw L3501 ; $30 not + defw L33C0 ; $31 duplicate + defw L36A0 ; $32 n-mod-m + defw L3686 ; $33 jump + defw L33C6 ; $34 stk-data + defw L367A ; $35 dec-jr-nz + defw L3506 ; $36 less-0 + defw L34F9 ; $37 greater-0 + defw L369B ; $38 end-calc + defw L3783 ; $39 get-argt + defw L3214 ; $3A truncate + defw L33A2 ; $3B fp-calc-2 + defw CALC_UNSUPPORTED ; $3C e-to-fp + defw L3297 ; $3D re-stack + defw L3449 ; series-xx $80-$9F + defw L341B ; stk-const-xx $A0-$BF + defw L342D ; st-mem-xx $C0-$DF + defw L340F ; get-mem-xx $E0-$FF +CALC_UNSUPPORTED: + ld a, ERROR_InvalidArg + jp __ERROR + ; --------------------------------------------------------------------------- + ; THE 'CALCULATE' SUBROUTINE ($335B) — motor principal + ; --------------------------------------------------------------------------- +L335B: + call L35BF +L335E: + ld a, b + ld (FP_BREG), a +L3362: + exx + ex (sp), hl + exx +L3365: + ld (FP_STKEND), de + exx + ld a, (hl) + inc hl +L336C: + push hl + and a + jp p, L3380 + ld d, a + and $60 + rrca + rrca + rrca + rrca + add a, $7C + ld l, a + ld a, d + and $1F + jr L338E +L3380: + cp $18 + jr nc, L338C + exx + ld bc, $FFFB + ld d, h + ld e, l + add hl, bc + exx +L338C: + rlca + ld l, a +L338E: + ld de, L32D7 + ld h, 0 + add hl, de + ld e, (hl) + inc hl + ld d, (hl) + ld hl, L3365 + ex (sp), hl + push de + exx + ld bc, (FP_STKEND + 1) ; C=STKEND_hi, B=FP_BREG (ver nota en sysvars.asm) + ret + ; --------------------------------------------------------------------------- + ; delete (literal $02) — un simple RET; tambien destino de salto indirecto + ; --------------------------------------------------------------------------- +L33A1: + ret + ; --------------------------------------------------------------------------- + ; fp-calc-2 (literal $3B) — reentrada de un solo literal + ; --------------------------------------------------------------------------- +L33A2: + pop af + ld a, (FP_BREG) + exx + jr L336C + ; --------------------------------------------------------------------------- + ; STK-PNTRS ($35BF) + ; --------------------------------------------------------------------------- +L35BF: + ld hl, (FP_STKEND) + ld de, $FFFB + push hl + add hl, de + pop de + ret + ; --------------------------------------------------------------------------- + ; jump-true (literal $00) / jump (literal $33) / dec-jr-nz (literal $35) + ; --------------------------------------------------------------------------- +L368F: + inc de + inc de + ld a, (de) + dec de + dec de + and a + jr nz, L3686 + exx + inc hl + exx + ret +L367A: + exx + push hl + ld hl, FP_BREG + dec (hl) + pop hl + jr nz, L3687 + inc hl + exx + ret +L3686: + exx +L3687: + ld e, (hl) + ld a, e + rla + sbc a, a + ld d, a + add hl, de + exx + ret + ; --------------------------------------------------------------------------- + ; end-calc (literal $38) + ; --------------------------------------------------------------------------- +L369B: + pop af + exx + ex (sp), hl + exx + ret + ; --------------------------------------------------------------------------- + ; n-mod-m (literal $32) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36A0: + rst 28h + defb $C0, $02, $31, $E0, $05, $27, $E0, $01, $C0, $04, $03, $E0, $38 + ret + ; --------------------------------------------------------------------------- + ; int (literal $27) — implementado como programa del propio calculador + ; --------------------------------------------------------------------------- +L36AF: + rst 28h + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L36B7 - $) & 0FFh ;;a X-NEG + defb $3A ;;truncate + defb $38 ;;end-calc + ret +L36B7: + defb $31 ;;duplicate + defb $3A ;;truncate + defb $C0 ;;st-mem-0 + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $01 ;;exchange + defb $30 ;;not + defb $00 ;;jump-true + defb (L36C2 - $) & 0FFh ;;a EXIT + defb $A1 ;;stk-one + defb $03 ;;subtract +L36C2: + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; TEST-ZERO ($34E9) / GREATER-0 (literal $37) / NOT (literal $30) / + ; less-0 (literal $36) / SIGN-TO-C / FP-0/1 + ; --------------------------------------------------------------------------- +L34E9: + push hl + push bc + ld b, a + ld a, (hl) + inc hl + or (hl) + inc hl + or (hl) + inc hl + or (hl) + ld a, b + pop bc + pop hl + ret nz + scf + ret +L34F9: + call L34E9 + ret c + ld a, $FF + jr L3507 +L3501: + call L34E9 + jr L350B +L3506: + xor a +L3507: + inc hl + xor (hl) + dec hl + rlca +L350B: + push hl + ld a, 0 + ld (hl), a + inc hl + ld (hl), a + inc hl + rla + ld (hl), a + rra + inc hl + ld (hl), a + inc hl + ld (hl), a + pop hl + ret + ; --------------------------------------------------------------------------- + ; or (literal $07) / no-&-no (literal $08) + ; --------------------------------------------------------------------------- +L351B: + ex de, hl + call L34E9 + ex de, hl + ret c + scf + jr L350B +L3524: + ex de, hl + call L34E9 + ex de, hl + ret nc + and a + jr L350B + ; --------------------------------------------------------------------------- + ; comparaciones numericas (literales $09-$0E, $353B) — solo la rama numerica; + ; no se soportan comparaciones de cadenas via calculador en este runtime. + ; --------------------------------------------------------------------------- +L353B: + ld a, b + sub 8 + bit 2, a + jr nz, L3543 + dec a +L3543: + rrca + jr nc, L354E + push af + push hl + call L343C + pop de + ex de, hl + pop af +L354E: + rrca + push af + call L300F + jr L358C + ; --------------------------------------------------------------------------- + ; END-TESTS ($358C) + ; --------------------------------------------------------------------------- +L358C: + pop af + push af + call c, L3501 + pop af + push af + call nc, L34F9 + pop af + rrca + call nc, L3501 + ret + ; =========================================================================== +; FASE 4: SIN/COS/TAN/ASN/ACS/ATN/LN/EXP/SQR + ; + ; Todas estas funciones son, igual que "int" o "n-mod-m" mas arriba, + ; programas del propio calculador (rst 28h + bytes de literal), identicos a + ; los de la ROM, apoyados en el "series generator" (L3449/L3453) ya portado. + ; Solo se han corregido los offsets de jump-true/jump (recalculados con la +; misma formula ya usada en el resto del fichero: (destino - $) & 0FFh) y se + ; ha sustituido "RST 08h ; DEFB " (ERROR-1 de la ROM) por el + ; mecanismo de error propio (ERROR_xxx + jp __ERROR). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STACK-A ($2D28) / STACK-BC ($2D2B) — apila A (o BC) como entero pequeño + ; --------------------------------------------------------------------------- +L2D28: + ld c, a + ld b, 0 +L2D2B: + xor a + ld e, a + ld d, c + ld c, b + ld b, a + call __FPSTACK_PUSH + rst 28h + defb $38 ;;end-calc (recalcula HL/DE tras el push) + and a + ret + ; --------------------------------------------------------------------------- + ; FP-TO-BC ($2DA2) — recoge el ultimo valor de la pila FP en BC (redondeando) + ; --------------------------------------------------------------------------- +L2DA2: + rst 28h + defb $38 ;;end-calc -> HL apunta al ultimo valor + ld a, (hl) + and a + jr z, L2DAD + rst 28h + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $38 ;;end-calc +L2DAD: + rst 28h + defb $02 ;;delete + defb $38 ;;end-calc + push hl + push de + ex de, hl + ld b, (hl) + call L2D7F + xor a + sub b + bit 7, c + ld b, d + ld c, e + ld a, e + pop de + pop hl + ret + ; --------------------------------------------------------------------------- + ; FP-TO-A ($2DD5) — como FP-TO-BC pero devuelve A, con overflow en carry + ; --------------------------------------------------------------------------- +L2DD5: + call L2DA2 + ret c + push af + dec b + inc b + jr z, L2DE1 + pop af + scf + ret +L2DE1: + pop af + ret + ; --------------------------------------------------------------------------- + ; get-argt (literal $39, $3783) — reduce el argumento de sin/cos a -1..+1 + ; --------------------------------------------------------------------------- +L3783: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $EE ;;Exponent: $7E, Bytes: 4 + defb $22, $F9, $83, $6E + defb $04 ;;multiply + defb $31 ;;duplicate + defb $A2 ;;stk-half + defb $0F ;;addition + defb $27 ;;int + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $0F ;;addition + defb $31 ;;duplicate + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $C0 ;;st-mem-0 + defb $00 ;;jump-true + defb (L37A1 - $) & 0FFh ;;a ZPLUS + defb $02 ;;delete + defb $38 ;;end-calc + ret +L37A1: + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $36 ;;less-0 + defb $00 ;;jump-true + defb (L37A8 - $) & 0FFh ;;a YNEG + defb $1B ;;negate +L37A8: + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; cos (literal $20, $37AA) — cae en sin/C-ENT (codigo compartido) + ; --------------------------------------------------------------------------- +L37AA: + rst 28h + defb $39 ;;get-argt + defb $2A ;;abs + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $E0 ;;get-mem-0 + defb $00 ;;jump-true + defb (L37B7 - $) & 0FFh ;;a C-ENT + defb $1B ;;negate + defb $33 ;;jump + defb (L37B7 - $) & 0FFh ;;a C-ENT + ; --------------------------------------------------------------------------- + ; sin (literal $1F, $37B5) / C-ENT ($37B7, compartido con cos) + ; --------------------------------------------------------------------------- +L37B5: + rst 28h + defb $39 ;;get-argt +L37B7: + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $86 ;;series-06 + defb $14, $E6 + defb $5C, $1F, $0B + defb $A3, $8F, $38, $EE + defb $E9, $15, $63, $BB, $23 + defb $EE, $92, $0D, $CD, $ED + defb $F1, $23, $5D, $1B, $EA + defb $04 ;;multiply + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; tan (literal $21, $37DA) — sin(x) / cos(x) + ; --------------------------------------------------------------------------- +L37DA: + rst 28h + defb $31 ;;duplicate + defb $1F ;;sin + defb $01 ;;exchange + defb $20 ;;cos + defb $05 ;;division + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; atn (literal $24, $37E2) + ; --------------------------------------------------------------------------- +L37E2: + call L3297 ; re-stack + ld a, (hl) + cp $81 + jr c, L37F8 ; SMALL + rst 28h + defb $A1 ;;stk-one + defb $1B ;;negate + defb $01 ;;exchange + defb $05 ;;division + defb $31 ;;duplicate + defb $36 ;;less-0 + defb $A3 ;;stk-pi/2 + defb $01 ;;exchange + defb $00 ;;jump-true + defb (L37FA - $) & 0FFh ;;a CASES + defb $1B ;;negate + defb $33 ;;jump + defb (L37FA - $) & 0FFh ;;a CASES +L37F8: + rst 28h + defb $A0 ;;stk-zero +L37FA: + defb $01 ;;exchange + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $8C ;;series-0C + defb $10, $B2 + defb $13, $0E + defb $55, $E4, $8D + defb $58, $39, $BC + defb $5B, $98, $FD + defb $9E, $00, $36, $75 + defb $A0, $DB, $E8, $B4 + defb $63, $42, $C4 + defb $E6, $B5, $09, $36, $BE + defb $E9, $36, $73, $1B, $5D + defb $EC, $D8, $DE, $63, $BE + defb $F0, $61, $A1, $B3, $0C + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; asn (literal $22, $3833) + ; --------------------------------------------------------------------------- +L3833: + rst 28h + defb $31 ;;duplicate + defb $31 ;;duplicate + defb $04 ;;multiply + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $1B ;;negate + defb $28 ;;sqr + defb $A1 ;;stk-one + defb $0F ;;addition + defb $05 ;;division + defb $24 ;;atn + defb $31 ;;duplicate + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; acs (literal $23, $3843) + ; --------------------------------------------------------------------------- +L3843: + rst 28h + defb $22 ;;asn + defb $A3 ;;stk-pi/2 + defb $03 ;;subtract + defb $1B ;;negate + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; ln (literal $25, $3713) + ; --------------------------------------------------------------------------- +L3713: + rst 28h + defb $3D ;;re-stack + defb $31 ;;duplicate + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L371C - $) & 0FFh ;;a VALID + defb $38 ;;end-calc + ld a, ERROR_InvalidArg + jp __ERROR +L371C: + defb $A0 ;;stk-zero + defb $02 ;;delete + defb $38 ;;end-calc + ld a, (hl) + ld (hl), $80 + call L2D28 + rst 28h + defb $34 ;;stk-data + defb $38 ;;Exponent: $88, Bytes: 1 + defb $00 + defb $03 ;;subtract + defb $01 ;;exchange + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $4C, $CC, $CC, $CD + defb $03 ;;subtract + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L373D - $) & 0FFh ;;a GRE.8 + defb $01 ;;exchange + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $01 ;;exchange + defb $38 ;;end-calc + inc (hl) + rst 28h +L373D: + defb $01 ;;exchange + defb $34 ;;stk-data + defb $F0 ;;Exponent: $80, Bytes: 4 + defb $31, $72, $17, $F8 + defb $04 ;;multiply + defb $01 ;;exchange + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $31 ;;duplicate + defb $34 ;;stk-data + defb $32 ;;Exponent: $82, Bytes: 1 + defb $20 + defb $04 ;;multiply + defb $A2 ;;stk-half + defb $03 ;;subtract + defb $8C ;;series-0C + defb $11, $AC + defb $14, $09 + defb $56, $DA, $A5 + defb $59, $30, $C5 + defb $5C, $90, $AA + defb $9E, $70, $6F, $61 + defb $A1, $CB, $DA, $96 + defb $A4, $31, $9F, $B4 + defb $E7, $A0, $FE, $5C, $FC + defb $EA, $1B, $43, $CA, $36 + defb $ED, $A7, $9C, $7E, $5E + defb $F0, $6E, $23, $80, $93 + defb $04 ;;multiply + defb $0F ;;addition + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; exp (literal $26, $36C4) + ; --------------------------------------------------------------------------- +L36C4: + rst 28h + defb $3D ;;re-stack + defb $34 ;;stk-data + defb $F1 ;;Exponent: $81, Bytes: 4 + defb $38, $AA, $3B, $29 + defb $04 ;;multiply + defb $31 ;;duplicate + defb $27 ;;int + defb $C3 ;;st-mem-3 + defb $03 ;;subtract + defb $31 ;;duplicate + defb $0F ;;addition + defb $A1 ;;stk-one + defb $03 ;;subtract + defb $88 ;;series-08 + defb $13, $36 + defb $58, $65, $66 + defb $9D, $78, $65, $40 + defb $A2, $60, $32, $C9 + defb $E7, $21, $F7, $AF, $24 + defb $EB, $2F, $B0, $B0, $14 + defb $EE, $7E, $BB, $94, $58 + defb $F1, $3A, $7E, $F8, $CF + defb $E3 ;;get-mem-3 + defb $38 ;;end-calc + call L2DD5 + jr nz, L3705 ; N-NEGTV + jr c, L3703 ; REPORT-6b + add a, (hl) + jr nc, L370C ; RESULT-OK +L3703: + ld a, ERROR_NumberTooBig + jp __ERROR +L3705: + jr c, L370E ; RSLT-ZERO + sub (hl) + jr nc, L370E ; RSLT-ZERO + neg +L370C: + ld (hl), a + ret +L370E: + rst 28h + defb $02 ;;delete + defb $A0 ;;stk-zero + defb $38 ;;end-calc + ret + ; --------------------------------------------------------------------------- + ; sqr (literal $28, $384A) — cae en to-power (codigo compartido) + ; --------------------------------------------------------------------------- +L384A: + rst 28h + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A2 ;;stk-half + defb $38 ;;end-calc + ; --------------------------------------------------------------------------- + ; to-power (literal $06, $3851) + ; --------------------------------------------------------------------------- +L3851: + rst 28h + defb $01 ;;exchange + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L385D - $) & 0FFh ;;a XIS0 + defb $25 ;;ln + defb $04 ;;multiply + defb $38 ;;end-calc + jp L36C4 +L385D: + defb $02 ;;delete + defb $31 ;;duplicate + defb $30 ;;not + defb $00 ;;jump-true + defb (L386A - $) & 0FFh ;;a ONE + defb $A0 ;;stk-zero + defb $01 ;;exchange + defb $37 ;;greater-0 + defb $00 ;;jump-true + defb (L386C - $) & 0FFh ;;a LAST + defb $A1 ;;stk-one + defb $01 ;;exchange + defb $05 ;;division +L386A: + defb $02 ;;delete + defb $A1 ;;stk-one +L386C: + defb $38 ;;end-calc + ret + ; =========================================================================== +; FASE 5: STK-TO-A / STK-TO-BC / CD-PRMS1 + ; + ; Rutinas auxiliares de la ROM usadas por DRAW3 (modo arco) y CIRCLE-DRAW. + ; No son literales del calculador (no se invocan via rst 28h + defb), sino + ; rutinas normales que a su vez usan el calculador ya portado (FP-TO-A, + ; STACK-A, y los literales sqr/sin/stk-data/etc de fp_calc.asm). + ; =========================================================================== + ; --------------------------------------------------------------------------- + ; STK-TO-A ($2314) — comprime el ultimo valor de la pila FP en A. + ; C = $01 si es positivo o cero, $FF si es negativo. + ; Error IntOutOfRange (sustituye a REPORT-Bc / RST 08h) si >= 256. + ; --------------------------------------------------------------------------- +L2314: + call L2DD5 ; FP-TO-A: A = valor comprimido, Z si signo positivo + jr c, L24F9 + ld c, $01 + ret z + ld c, $FF + ret +L24F9: + ld a, ERROR_IntOutOfRange + jp __ERROR + ; --------------------------------------------------------------------------- +; STK-TO-BC ($2307) — recoge dos valores de la pila FP: el primero (mas + ; antiguo) en BC, el segundo (mas reciente) en DE (bajo, con signo en E/D). + ; --------------------------------------------------------------------------- +L2307: + call L2314 + ld b, a + push bc + call L2314 + ld e, c + pop bc + ld d, c + ld c, a + ret + ; --------------------------------------------------------------------------- +; CD-PRMS1 ($247D) — CIRCLE/DRAW PARAMETERS: a partir del "diametro" z (tope + ; de pila) y el angulo total en mem-5, calcula el numero de lineas rectas + ; (B, multiplo de 4, max 252) y deja en mem-1/mem-3/mem-4 sin(a/2), cos(a) y + ; sin(a) del angulo de paso "a" = ANGULO/lineas. + ; --------------------------------------------------------------------------- +L247D: + rst 28h + defb $31 ;;duplicate z, z. + defb $28 ;;sqr z, sqr(z). + defb $34 ;;stk-data z, sqr(z), 2. + defb $32 ;;Exponent: $82, Bytes: 1 + defb $00 ;;(+00,+00,+00) + defb $01 ;;exchange z, 2, sqr(z). + defb $05 ;;division z, 2/sqr(z). + defb $E5 ;;get-mem-5 z, 2/sqr(z), ANGLE. + defb $01 ;;exchange z, ANGLE, 2/sqr(z) + defb $05 ;;division z, ANGLE*sqr(z)/2 (=num. lineas) + defb $2A ;;abs (solo para arco) + defb $38 ;;end-calc z, numero de lineas. + call L2DD5 ; FP-TO-A + jr c, L247D_USE252 + and $FC ; multiplo de 4 (p.ej. 29 -> 28) + add a, $04 ; podria dar overflow -> 256 + jr nc, L247D_SAVE +L247D_USE252: + ld a, $FC ; limite de 252 (para arco) +L247D_SAVE: + push af ; conserva el contador de lineas + call L2D28 ; apila el contador modificado + rst 28h + defb $E5 ;;get-mem-5 z, A, ANGLE. + defb $01 ;;exchange z, ANGLE, A. + defb $05 ;;division z, ANGLE/A. (angulo de paso = a) + defb $31 ;;duplicate z, a, a. + defb $1F ;;sin z, a, sin(a) + defb $C4 ;;st-mem-4 z, a, sin(a) + defb $02 ;;delete z, a. + defb $31 ;;duplicate z, a, a. + defb $A2 ;;stk-half z, a, a, 1/2. + defb $04 ;;multiply z, a, a/2. + defb $1F ;;sin z, a, sin(a/2). + defb $C1 ;;st-mem-1 z, a, sin(a/2). + defb $01 ;;exchange z, sin(a/2), a. + defb $C0 ;;st-mem-0 z, sin(a/2), a. (solo para arco) + defb $02 ;;delete z, sin(a/2). + defb $31 ;;duplicate z, sin(a/2), sin(a/2). + defb $04 ;;multiply z, sin(a/2)^2. + defb $31 ;;duplicate z, sin(a/2)^2, sin(a/2)^2. + defb $0F ;;addition z, 2*sin(a/2)^2. + defb $A1 ;;stk-one z, 2*sin(a/2)^2, 1. + defb $03 ;;subtract z, 2*sin(a/2)^2-1. + defb $1B ;;negate z, 1-2*sin(a/2)^2 = cos(a). + defb $C3 ;;st-mem-3 z, cos(a). + defb $02 ;;delete z. + defb $38 ;;end-calc z. + pop bc ; restaura el contador de lineas + ret + pop namespace +#line 21 "/zxbasic/src/lib/arch/zx81sd/runtime/bootstrap.asm" + push namespace core + ; SD81_INIT_SYSVARS — Inicializa el bloque de variables del runtime en $8000 +SD81_INIT_SYSVARS: + PROC + ; CHARS apunta 256 bytes ANTES del inicio del font (convención Spectrum): + ; el runtime calcula glifo = CHARS + código*8, sin restar 32. + ; Así CHR$(32)=space → CHARS+256 = font[0], CHR$(72)='H' → CHARS+576 = font[40]. + ld hl, __ZX81SD_CHARSET - 256 + ld (CHARS), hl + ; UDG: área dedicada de 21 caracteres definibles (CHR$(144)-CHR$(164), + ; como en el Spectrum), reservada en charset.asm DESPUÉS de la fuente. + ; (La fuente sólo cubre CHR$(32)-CHR$(127): el antiguo "font+896" + ; apuntaba 128 bytes más allá de su final, sobre código del runtime, + ; y los POKE USR CHR$ de un programa lo corrompían.) + ld hl, __ZX81SD_UDG_AREA + ld (UDG), hl + ; Inicializa los UDGs con copias de las letras A-U (como la ROM del + ; Spectrum): glifo de 'A' = font + (65-32)*8 = font + 264. + ld hl, __ZX81SD_CHARSET + 264 + ld de, __ZX81SD_UDG_AREA + ld bc, 21 * 8 + ldir + ; Cursor al inicio de pantalla (columna=SCR_COLS, fila=SCR_ROWS) + ld a, SCR_ROWS + ld h, a + ld a, SCR_COLS + ld l, a + ld (S_POSN), hl + ; SCREEN_ADDR / SCREEN_ATTR_ADDR son variables RAM (no constantes EQU): + ; el runtime las lee con LD HL,(SCREEN_ADDR) para obtener la dirección. + ld hl, $C000 + ld (SCREEN_ADDR), hl ; $801E ← $C000 + ld (DFCC), hl ; cursor bitmap al inicio de pantalla + ld hl, $D800 + ld (SCREEN_ATTR_ADDR), hl ; $8020 ← $D800 + ld (DFCCL), hl ; cursor attrs al inicio de atributos + ; COORDS: último punto PLOT = (0,0) + xor a + ld (COORDS), a + ld (COORDS + 1), a + ; Atributos por defecto: tinta negra sobre fondo blanco (INK 0, PAPER 7) + ; $38 = 0b00111000 = PAPER 7 + INK 0, igual que el defecto del Spectrum + ld a, $38 + ld (ATTR_P), a + xor a + ld (MASK_P), a ; $00 = sin transparencia (COPY_ATTR copia ATTR_P íntegro) + ld hl, $0038 ; ATTR_T=$38, MASK_T=$00 + ld (ATTR_T), hl + ; Borra la pantalla física (bitmap + atributos). El bloque 6 es RAM + ; que sobrevive a un reset (no se limpia sola como al encender una + ; ROM real), así que sin esto un programa hereda el contenido que + ; dejó el anterior hasta que él mismo llame a CLS. + ld hl, $C000 + ld (hl), 0 + ld d, h + ld e, l + inc de + ld bc, 6143 + ldir + ld hl, $D800 + ld a, (ATTR_P) + ld (hl), a + ld d, h + ld e, l + inc de + ld bc, 767 + ldir + ; Flags a cero + xor a + ld (FLAGS2), a + ld (P_FLAG), a + ld (TV_FLAG), a + ld (ERR_NR), a + ; Contadores a cero + ld hl, 0 + ld (FRAMES), hl + ld (RANDOM_SEED_LOW), hl + ; Calculador de coma flotante (fp_calc.asm): pila FP vacía, MEM en su + ; posición por defecto (ver sysvars.asm) + ld hl, FP_CALC_STACK + ld (FP_STKBOT), hl + ld (FP_STKEND), hl + ld hl, FP_MEM_AREA + ld (FP_MEM), hl + ret + ENDP + pop namespace +#line 17 "/zxbasic/src/lib/arch/zx81sd/runtime/sysvars.asm" + push namespace core + ; --- Variables dinámicas del runtime ($8000+) --------------------------- + ; Zona de datos (bloques 4-5), no ejecutable sin MC45. + ; + ; SCREEN_ADDR y SCREEN_ATTR_ADDR son variables RAM (no constantes EQU) +; porque el runtime de zx48k las lee con direccionamiento indirecto: + ; LD HL, (SCREEN_ADDR) → carga el CONTENIDO de esa posición de memoria. + ; SD81_INIT_SYSVARS las inicializa con $C000 y $D800 respectivamente. + SYSVAR_BASE EQU $8000 + CHARS EQU SYSVAR_BASE + $00 ; DW — puntero a charset (8x8) + UDG EQU SYSVAR_BASE + $02 ; DW — puntero a UDGs + COORDS EQU SYSVAR_BASE + $04 ; DW — última coordenada PLOT (X,Y) + FLAGS2 EQU SYSVAR_BASE + $06 ; DB — flags de pantalla (OVER/INVERSE/etc.) + ECHO_E EQU SYSVAR_BASE + $07 ; DB — (reservado) + DFCC EQU SYSVAR_BASE + $08 ; DW — siguiente dirección bitmap para PRINT + DFCCL EQU SYSVAR_BASE + $0A ; DW — siguiente dirección attrs para PRINT + S_POSN EQU SYSVAR_BASE + $0C ; DW — posición cursor (H=fila, L=columna) + ATTR_P EQU SYSVAR_BASE + $0E ; DB — atributo permanente (INK/PAPER/etc.) + MASK_P EQU SYSVAR_BASE + $0F ; DB — máscara permanente ($00 = sin transparencia) + ATTR_T EQU SYSVAR_BASE + $10 ; DB — atributo temporal + ; MASK_T se accede implícitamente como ATTR_T+1 ($8011) via LD HL,(ATTR_T) + P_FLAG EQU SYSVAR_BASE + $12 ; DB — flags de impresión (OVER/INVERSE perm.) + MEM0 EQU SYSVAR_BASE + $13 ; 5B — buffer temporal para rutinas gráficas + TV_FLAG EQU SYSVAR_BASE + $18 ; DB — flags de control de salida a pantalla + ERR_NR EQU SYSVAR_BASE + $19 ; DB — código de error (-1 = sin error) + FRAMES EQU SYSVAR_BASE + $1A ; DW — contador de frames VSYNC (software) + RANDOM_SEED_LOW EQU SYSVAR_BASE + $1C ; DW — semilla RNG (16 bits bajos) +SCREEN_ADDR EQU SYSVAR_BASE + $1E ; DW — puntero al framebuffer (init: $C000) +SCREEN_ATTR_ADDR EQU SYSVAR_BASE + $20 ; DW — puntero a atributos (init: $D800) + ; --- Sysvars del calculador de coma flotante (fp_calc.asm) -------------- + ; Equivalentes a STKBOT/STKEND/BREG/MEM de la ROM Spectrum ($5C63/$5C65/ + ; $5C67/$5C68), pero apuntando a un buffer fijo propio en vez de al área + ; de trabajo dinámica de la ROM (aquí no existe "memoria libre creciente" + ; entre el programa y la pila de máquina). + ; +; IMPORTANTE: FP_BREG debe estar INMEDIATAMENTE DESPUÉS de FP_STKEND — el + ; motor CALCULATE (L338E, ENT-TABLE) explota la contigüidad de memoria de + ; la ROM original (STKEND_hi seguido de BREG) para cargar ambos con un +; único LD BC,(FP_STKEND+1): C=STKEND_hi, B=BREG. No reordenar. + FP_STKBOT EQU SYSVAR_BASE + $22 ; DW — base de la pila de números FP + FP_STKEND EQU SYSVAR_BASE + $24 ; DW — siguiente posición libre en la pila FP + FP_BREG EQU SYSVAR_BASE + $26 ; DB — literal en curso (para fp-calc-2/dec-jr-nz) + FP_MEM EQU SYSVAR_BASE + $27 ; DW — puntero al área MEM (6 celdas de 5B) + FP_CALC_STACK EQU SYSVAR_BASE + $29 ; 60B — pila de números FP (12 números máx.) + FP_CALC_STACK_END EQU FP_CALC_STACK + 60 + FP_MEM_AREA EQU SYSVAR_BASE + $65 ; 30B — área MEM (6 celdas de 5 bytes) + ; --- Scratch del indexador de arrays (runtime/array/array.asm) --------- + ; El array.asm compartido de zx48k usa la sysvar MEMBOT de la ROM Spectrum + ; (dirección fija 23698 = $5C92) como almacenamiento temporal para sus + ; punteros (LBOUND_PTR/UBOUND_PTR/RET_ADDR/TMP_ARR_PTR, 2 bytes cada uno). + ; En zx81sd esa dirección cae dentro del propio código compilado del +; programa (bloque 2, $4000-$5FFF): cualquier array multidimensional lo + ; corrompía en cada acceso. El override zx81sd de array.asm usa esta + ; zona en su lugar. + ARRAY_SCRATCH EQU SYSVAR_BASE + $83 ; 8B — LBOUND/UBOUND/RET/TMP_ARR_PTR + ; --- Scratch de CHR$() (runtime/chr.asm) -------------------------------- + ; El chr.asm compartido de zx48k usa la sysvar DEST de la ROM Spectrum + ; (dirección fija 23629 = $5C4D) para guardar temporalmente la dirección + ; de retorno mientras reserva memoria para la cadena. Esa dirección cae + ; dentro del código compilado en zx81sd (bloque 2, en concreto dentro de + ; __DIVU32). El override zx81sd de chr.asm usa esta zona en su lugar. + CHR_SCRATCH EQU SYSVAR_BASE + $8B ; 2B — dirección de retorno + ; --- Scratch de la división FP (runtime/arith/divf.asm) ----------------- + ; El divf.asm compartido de zx48k usa DEST (23629, igual que chr.asm) y + ; ERR_SP (23613) de la ROM Spectrum para guardar/restaurar un punto de + ; recuperación de pila alrededor de la división en coma flotante (truco + ; de "longjmp" para división por cero). En zx81sd ese mecanismo de + ; recuperación no llega a usarse de verdad (__ERROR hace DI+HALT + ; directamente, no restaura SP desde ERR_SP), pero el código escribe ahí + ; en TODAS las divisiones igualmente, no solo cuando hay error — y esas + ; direcciones caen dentro del código compilado (bloque 2). El override + ; zx81sd usa esta zona en su lugar (mismo mecanismo, solo cambia dónde + ; vive el scratch). + DIVF_SCRATCH EQU SYSVAR_BASE + $8D ; 4B — TMP (2B) + ERR_SP (2B) +; Tamaño total del bloque de sysvars: $91 bytes + ; --- Constantes de pantalla --------------------------------------------- + SCR_COLS EQU 33 ; Columnas + 1 (32 columnas visibles) + SCR_ROWS EQU 24 ; Filas (24 filas visibles) + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 7 "/zxbasic/src/lib/arch/zx81sd/runtime/error.asm" + push namespace core + ; Códigos de error (compatibles con el manual del ZX Spectrum) + 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 + ; __ERROR — Detiene la ejecución con un código de error en A. +; Sustituye a: RST 8 (ROM Spectrum) +__ERROR: + ld (__ERROR_CODE), a + ld (ERR_NR), a ; guardar en sysvar + di ; asegurar interrupciones desactivadas + halt ; detener CPU +__ERROR_CODE: + nop ; byte de código de error (para compatibilidad con llamadores) + ret ; no se alcanza, pero mantiene la estructura original + ; __STOP — Guarda el código de error y continúa (para END del programa). +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 69 "/zxbasic/src/lib/arch/zx48k/runtime/mem/alloc.asm" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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/zx48k/runtime/mem/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/zx48k/runtime/mem/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 13 "/zxbasic/src/lib/arch/zx48k/runtime/mem/calloc.asm" + ; --------------------------------------------------------------------- + ; MEM_CALLOC + ; Allocates a block of memory in the heap, and clears it filling it + ; with 0 bytes + ; + ; 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_CALLOC: + push bc + call __MEM_ALLOC + pop bc + ld a, h + or l + ret z ; No memory + ld (hl), 0 + dec bc + ld a, b + or c + ret z ; Already filled (1 byte-length block) + ld d, h + ld e, l + inc de + push hl + ldir + pop hl + ret + pop namespace +#line 3 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- + push namespace core +__ALLOC_LOCAL_ARRAY: + push de + push ix + pop de + add hl, de ; hl = ix + hl + pop de + ld (hl), e + inc hl + ld (hl), d + inc hl + push hl + call __MEM_CALLOC + pop de + ex de, hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY + ; Allocates an array element area in the heap, and clears it filling it + ; with data whose pointer (PTR) is in the stack + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] = PTR to the element area + ; +; Returns: + ; HL = (IX + HL) + 4 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY: + push bc + call __ALLOC_LOCAL_ARRAY + pop bc + ;; Swaps [SP], [SP + 2] + exx + pop hl ; HL <- RET address + ex (sp), hl ; HL <- Data table, [SP] <- RET address + push hl ; [SP] <- Data table + exx + ex (sp), hl ; HL = Data table, (SP) = (IX + HL + 4) - start of array address lbound + ; HL = data table + ; BC = length + ; DE = new data area + ldir + pop hl ; HL = addr of LBound area if used + ret + ; --------------------------------------------------------------------- + ; __ALLOC_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes. Then sets LBOUND and UBOUND ptrs + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; [SP + 2] PTR to the lbound element area + ; [SP + 4] PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS: + call __ALLOC_LOCAL_ARRAY +__ALLOC_LOCAL_ARRAY_WITH_BOUNDS2: + pop bc ;; ret address + pop de ;; lbound + inc hl + ld (hl), e + inc hl + ld (hl), d + pop de ;; PTR to ubound table + push bc ;; puts ret address back + ld a, d + or e + ret z ;; if PTR for UBound is 0, it's not used + inc hl + ld (hl), e + inc hl + ld (hl), d + ret + ; --------------------------------------------------------------------- + ; __ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS + ; Allocates an array element area in the heap, and clears it filling it + ; with 0 bytes + ; + ; Parameters + ; HL = Offset to be added to IX => HL = IX + HL + ; BC = Length of the element area = n.elements * size(element) + ; DE = PTR to the index table + ; TOP of the stack = PTR to the element area + ; [SP + 2] = PTR to the element area + ; [SP + 4] = PTR to the lbound element area + ; [SP + 6] = PTR to the ubound element area + ; +; Returns: + ; HL = (IX + HL) + 8 + ; --------------------------------------------------------------------- +__ALLOC_INITIALIZED_LOCAL_ARRAY_WITH_BOUNDS: + ;; Swaps [SP] and [SP + 2] + exx + pop hl ;; Ret address + ex (sp), hl ;; HL <- PTR to Element area, (sp) = Ret address + push hl ;; [SP] = PTR to element area, [SP + 2] = Ret address + exx + call __ALLOC_INITIALIZED_LOCAL_ARRAY + jp __ALLOC_LOCAL_ARRAY_WITH_BOUNDS2 +#line 142 "/zxbasic/src/lib/arch/zx48k/runtime/array/arrayalloc.asm" + pop namespace +#line 102 "arch/zx81sd/ubound12.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraybound.asm" + ; --------------------------------------------------------- + ; Copyleft (k)2011 by Jose Rodriguez (a.k.a. Boriel) +; http://www.boriel.com + ; +; ZX BASIC Compiler http://www.zxbasic.net + ; This code is released under the BSD License + ; --------------------------------------------------------- + ; Implements both LBOUND(array, N) and UBOUND(array, N) function +; Parameters: + ; HL = PTR to array + ; [stack - 2] -> N (dimension) + push namespace core + PROC + LOCAL __BOUND + LOCAL __DIM_NOT_EXIST + LOCAL __CONT +__LBOUND: + ld a, 4 + jr __BOUND +__UBOUND: + ld a, 6 +__BOUND: + ex de, hl ; DE <-- Array ptr + pop hl ; HL <-- Ret address + ex (sp), hl ; CALLEE: HL <-- N, (SP) <-- Ret address + ex de, hl ; DE <-- N, HL <-- ARRAY_PTR + push hl + ld c, (hl) + inc hl + ld h, (hl) + ld l, c ; HL = start of dimension table (first position contains number of dimensions - 1) + ld c, (hl) + inc hl + ld b, (hl) + inc bc ; Number of total dimensions of the array + pop hl ; Recovers ARRAY PTR + ex af, af' ; Saves A for later + ld a, d + or e + jr nz, __CONT ; N = 0 => Return number of dimensions + ;; Return the number of dimensions of the array + ld h, b + ld l, c + ret +__CONT: + dec de + ex af, af' ; Recovers A (contains PTR offset) + ex de, hl ; HL = N (dimension asked) - 1, DE = Array PTR + or a + sbc hl, bc ; if no Carry => the user asked for a dimension that does not exist. Return 0 + jr nc, __DIM_NOT_EXIST + add hl, bc ; restores HL = (N - 1) + add hl, hl ; hl *= 2 + ex de, hl ; hl = ARRAY_PTR + 3, DE jsz = (N - 1) * 2 + ld b, 0 + ld c, a + add hl, bc ; HL = &BOUND_PTR + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; LD HL, (HL) => Origin of L/U Bound table + ; for LBound only, HL = 0x0000 (NULL) if the array is all 0-based + or h + ret z ; Should never happen for UBound + add hl, de ; hl += OFFSET __LBOUND._xxxx + ld e, (hl) ; de = (hl) + inc hl + ld d, (hl) + ex de, hl ; hl = de => returns result in HL + ret +__DIM_NOT_EXIST: + ; The dimension requested by the user does not exists. Return 0 + ld hl, 0 + ret + ENDP + pop namespace +#line 103 "arch/zx81sd/ubound12.bas" +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" + ; This routine is in charge of freeing an array of strings from memory + ; HL = Pointer to start of array in memory + ; Top of the stack = Number of elements of the array +#line 1 "/zxbasic/src/lib/arch/zx48k/runtime/mem/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 6 "/zxbasic/src/lib/arch/zx48k/runtime/array/arraystrfree.asm" + push namespace core +__ARRAYSTR_FREE: + PROC + LOCAL __ARRAY_LOOP + ex de, hl + pop hl ; (ret address) + ex (sp), hl ; Callee -> HL = Number of elements + ex de, hl +__ARRAYSTR_FREE_FAST: ; Fastcall entry: DE = Number of elements + ld a, h + or l + ret z ; ret if NULL + ld b, d + ld c, e +__ARRAY_LOOP: + ld e, (hl) + inc hl + ld d, (hl) + inc hl ; DE = (HL) = String Pointer + push hl + push bc + ex de, hl + call __MEM_FREE ; Frees it from memory + pop bc + pop hl + dec bc + ld a, b + or c + jp nz, __ARRAY_LOOP + ret ; Frees it and return + ENDP +__ARRAYSTR_FREE_MEM: ; like the above, buf also frees the array itself + ex de, hl + pop hl ; (ret address) + ex (sp), hl ; Callee -> HL = Number of elements + ex de, hl + push hl ; Saves array pointer for later + call __ARRAYSTR_FREE_FAST + pop hl ; recovers array block pointer + jp __MEM_FREE ; Frees it and returns from __MEM_FREE + pop namespace +#line 104 "arch/zx81sd/ubound12.bas" +.LABEL.__LABEL5: + DEFB 01h + DEFB 00h + DEFB 03h + DEFB 00h + DEFB 02h + END diff --git a/tests/functional/arch/zx81sd/ubound12.bas b/tests/functional/arch/zx81sd/ubound12.bas new file mode 100644 index 000000000..f09ace4f4 --- /dev/null +++ b/tests/functional/arch/zx81sd/ubound12.bas @@ -0,0 +1,21 @@ + +DIM b, c as UInteger + +DECLARE SUB test2(a2() as String) + +SUB test3(a3() as String) + FOR b = 0 TO 3 + c = UBound(a3, b) + NEXT +END SUB + +SUB test1() + DIM a1(3 TO 5, 7 TO 9) As String + test2(a1) +END SUB + +SUB test2(a2() as String) + test3(a2) +END SUB + +test1()