diff --git a/src/arch/z80/backend/common.py b/src/arch/z80/backend/common.py index 6f25dc8d2..c6eb7a969 100644 --- a/src/arch/z80/backend/common.py +++ b/src/arch/z80/backend/common.py @@ -402,7 +402,18 @@ def to_long(stype: DataType) -> list[str]: ) elif stype == F16_t: - output.extend(["ex de, hl", "ld de, 0"]) + output.extend( + [ + "ex de, hl", + "ld de, 0", + # Copies the highest bit (sign) to DE + "ld a, h", + "add a, a", + "sbc a, a", + "ld e, a", + "ld d, a", + ] + ) elif stype in (U32_t, I32_t): return [] diff --git a/src/symbols/typecast.py b/src/symbols/typecast.py index 664e801fb..31a2d30d2 100644 --- a/src/symbols/typecast.py +++ b/src/symbols/typecast.py @@ -4,6 +4,7 @@ # See the file CONTRIBUTORS.md for copyright details. # See https://www.gnu.org/licenses/agpl-3.0.html for details. # -------------------------------------------------------------------- +import math from src.api import check, errmsg from src.api.errmsg import error @@ -91,7 +92,8 @@ def make_node(cls, new_type: SymbolTYPE, node: Symbol, lineno: int): elif new_type.is_basic and not TYPE.is_integral(new_type): # not an integer node.value = float(node.value) else: # It's an integer - new_val = int(node.value) & ((1 << (8 * new_type.size)) - 1) # Mask it + # ZX Spectrum ROM always truncates to -Infinity, so we do the same using floor() + new_val = math.floor(node.value) & ((1 << (8 * new_type.size)) - 1) # Mask it if node.value >= 0 and node.value != new_val: errmsg.warning_conversion_lose_digits(node.lineno) diff --git a/src/zxbc/zxblex.py b/src/zxbc/zxblex.py index ffd54f741..9d739029f 100755 --- a/src/zxbc/zxblex.py +++ b/src/zxbc/zxblex.py @@ -14,8 +14,7 @@ from src.api import global_ from src.api.errmsg import error from src.ply import lex - -from .keywords import KEYWORDS as reserved +from src.zxbc.keywords import KEYWORDS as reserved ASM = "" # Set to asm block when commenting ASMLINENO = 0 # Line of ASM INLINE beginning diff --git a/tests/functional/arch/zx48k/cast_const_to_int.asm b/tests/functional/arch/zx48k/cast_const_to_int.asm new file mode 100644 index 000000000..2ccdf1e0a --- /dev/null +++ b/tests/functional/arch/zx48k/cast_const_to_int.asm @@ -0,0 +1,232 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_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 + ld hl, (.core.__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret + ;; --- 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 92 "arch/zx48k/cast_const_to_int.bas" + END diff --git a/tests/functional/arch/zx48k/cast_const_to_int.bas b/tests/functional/arch/zx48k/cast_const_to_int.bas new file mode 100644 index 000000000..25d7b2b04 --- /dev/null +++ b/tests/functional/arch/zx48k/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/zx48k/cast_f16_to_long.asm b/tests/functional/arch/zx48k/cast_f16_to_long.asm index 547845bad..c83500765 100644 --- a/tests/functional/arch/zx48k/cast_f16_to_long.asm +++ b/tests/functional/arch/zx48k/cast_f16_to_long.asm @@ -29,6 +29,11 @@ _v2: 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 diff --git a/tests/functional/arch/zx48k/cast_f16_to_ulong.asm b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm index 547845bad..c83500765 100644 --- a/tests/functional/arch/zx48k/cast_f16_to_ulong.asm +++ b/tests/functional/arch/zx48k/cast_f16_to_ulong.asm @@ -29,6 +29,11 @@ _v2: 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 diff --git a/tests/functional/arch/zxnext/cast_const_to_int.asm b/tests/functional/arch/zxnext/cast_const_to_int.asm new file mode 100644 index 000000000..035405911 --- /dev/null +++ b/tests/functional/arch/zxnext/cast_const_to_int.asm @@ -0,0 +1,225 @@ + org 32768 +.core.__START_PROGRAM: + di + push iy + ld iy, 0x5C3A ; ZX Spectrum ROM variables address + ld (.core.__CALL_BACK__), sp + ei + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_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 + ld hl, (.core.__CALL_BACK__) + ld sp, hl + pop iy + ei + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/ftou32reg.asm" +#line 1 "/zxbasic/src/lib/arch/zxnext/runtime/neg32.asm" + push namespace core +__ABS32: + bit 7, d + ret z +__NEG32: ; Negates DEHL (Two's complement) + ld a, l + cpl + ld l, a + ld a, h + cpl + ld h, a + ld a, e + cpl + ld e, a + ld a, d + cpl + ld d, a + inc l + ret nz + inc h + ret nz + inc de + ret + pop namespace +#line 2 "/zxbasic/src/lib/arch/zxnext/runtime/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 88 "arch/zxnext/cast_const_to_int.bas" + END diff --git a/tests/functional/arch/zxnext/cast_const_to_int.bas b/tests/functional/arch/zxnext/cast_const_to_int.bas new file mode 100644 index 000000000..25d7b2b04 --- /dev/null +++ b/tests/functional/arch/zxnext/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/zxnext/cast_f16_to_long.asm b/tests/functional/arch/zxnext/cast_f16_to_long.asm index 53c65b0ce..6c0c77264 100644 --- a/tests/functional/arch/zxnext/cast_f16_to_long.asm +++ b/tests/functional/arch/zxnext/cast_f16_to_long.asm @@ -26,6 +26,11 @@ _v2: 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 diff --git a/tests/functional/arch/zxnext/cast_f16_to_ulong.asm b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm index 53c65b0ce..6c0c77264 100644 --- a/tests/functional/arch/zxnext/cast_f16_to_ulong.asm +++ b/tests/functional/arch/zxnext/cast_f16_to_ulong.asm @@ -26,6 +26,11 @@ _v2: 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