diff --git a/src/api/check.py b/src/api/check.py index a40eec425..9083902e9 100644 --- a/src/api/check.py +++ b/src/api/check.py @@ -320,6 +320,10 @@ def is_number(*p): return all(i.token in ("NUMBER", "CONST") for i in p) +def is_static_str(*p): + return all(i.token == "STRING" for i in p) + + def is_var(*p): """Returns True if ALL the arguments are AST nodes containing ID diff --git a/src/arch/z80/backend/common.py b/src/arch/z80/backend/common.py index 7373cb836..604fc8463 100644 --- a/src/arch/z80/backend/common.py +++ b/src/arch/z80/backend/common.py @@ -8,6 +8,7 @@ from src.api import global_, tmp_labels from src.api.exception import TempAlreadyFreedError +from src.symbols.symbol_ import Symbol from . import exception from .exception import InvalidICError as InvalidIC @@ -110,7 +111,7 @@ def __init__(self, *args): if len(args) - 1 != QUADS[args[0]].nargs: exception.throw_invalid_quad_params(args[0], len(args) - 1, QUADS[args[0]].nargs) - args = tuple([str(x) for x in args]) # Convert it to strings + args = tuple([str(x.t if isinstance(x, Symbol) else x) for x in args]) # Convert it to strings self.quad = args self.op = args[0] diff --git a/src/arch/z80/translator.py b/src/arch/z80/translator.py index 67797214d..4d5a1c00a 100644 --- a/src/arch/z80/translator.py +++ b/src/arch/z80/translator.py @@ -94,7 +94,7 @@ def visit_LABEL(self, node): self.ic_label(node.mangled) def visit_CONST(self, node): - yield node.t + yield node.symbol def visit_VAR(self, node): __DEBUG__( @@ -977,7 +977,7 @@ def loop_cont_label(self, loop_type): raise InvalidLoopError(loop_type) @classmethod - def default_value(cls, type_, expr): # TODO: This function must be moved to api.xx + def default_value(cls, type_: symbols.TYPE, expr) -> list[str]: # TODO: This function must be moved to api.xx """Returns a list of bytes (as hexadecimal 2 char string)""" assert isinstance(type_, symbols.TYPE) assert type_.is_basic @@ -986,44 +986,37 @@ def default_value(cls, type_, expr): # TODO: This function must be moved to api if expr.token in ("CONSTEXPR", "CONST"): # a constant expression like @label + 1 if type_ in (cls.TYPE(TYPE.float), cls.TYPE(TYPE.string)): error(expr.lineno, f"Can't convert non-numeric value to {type_.name} at compile time") - return [""] + return [""] # dummy placeholder so the compilation continues val = Translator.traverse_const(expr) if type_.size == 1: # U/byte if expr.type_.size != 1: - return ["#({0}) & 0xFF".format(val)] + return [f"#({val}) & 0xFF"] else: - return ["#{0}".format(val)] + return [f"#{val}"] if type_.size == 2: # U/integer if expr.type_.size != 2: - return ["##({0}) & 0xFFFF".format(val)] + return [f"##({val}) & 0xFFFF"] else: - return ["##{0}".format(val)] + return [f"##{val}"] if type_ == cls.TYPE(TYPE.fixed): - return ["0000", "##({0}) & 0xFFFF".format(val)] + return ["0000", f"##({val}) & 0xFFFF"] # U/Long - return ["##({0}) & 0xFFFF".format(val), "##(({0}) >> 16) & 0xFFFF".format(val)] + return [f"##({val}) & 0xFFFF", f"##(({val}) >> 16) & 0xFFFF"] if type_ == cls.TYPE(TYPE.float): C, DE, HL = _float(expr.value) C = C[:-1] # Remove 'h' suffix - if len(C) > 2: - C = C[-2:] + C = C[-2:] DE = DE[:-1] # Remove 'h' suffix - if len(DE) > 4: - DE = DE[-4:] - elif len(DE) < 3: - DE = "00" + DE + DE = ("00" + DE)[-4:] HL = HL[:-1] # Remove 'h' suffix - if len(HL) > 4: - HL = HL[-4:] - elif len(HL) < 3: - HL = "00" + HL + HL = ("00" + HL)[-4:] return [C, DE[-2:], DE[:-2], HL[-2:], HL[:-2]] @@ -1032,8 +1025,8 @@ def default_value(cls, type_, expr): # TODO: This function must be moved to api else: value = int(expr.value) - result = [value, value >> 8, value >> 16, value >> 24] - result = ["%02X" % (v & 0xFF) for v in result] + values = [value, value >> 8, value >> 16, value >> 24] + result = ["%02X" % (v & 0xFF) for v in values] return result[: type_.size] @staticmethod @@ -1459,7 +1452,10 @@ def visit_FUNCTION(self, node): if node.convention == CONVENTION.stdcall: for local_var in node.local_symbol_table.values(): scope = local_var.scope - if local_var.type_ == self.TYPE(TYPE.string): # Only if it's string we free it + if local_var.type_ == self.TYPE(TYPE.string): + if local_var.class_ == CLASS.const: + continue + # Only if it's string we free it if local_var.class_ != CLASS.array: # Ok just free it if scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref): if not preserve_hl: @@ -1469,8 +1465,6 @@ def visit_FUNCTION(self, node): offset = -local_var.offset if scope == SCOPE.local else local_var.offset self.ic_fpload(TYPE.string, local_var.t, offset) self.runtime_call(RuntimeLabel.MEM_FREE, 0) - elif local_var.class_ == CLASS.const: - continue else: # This is an array of strings, we must free it unless it's a by_ref array if scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref): if not preserve_hl: diff --git a/src/symbols/id_/ref/constref.py b/src/symbols/id_/ref/constref.py index b8bd44955..c57d50089 100644 --- a/src/symbols/id_/ref/constref.py +++ b/src/symbols/id_/ref/constref.py @@ -9,7 +9,7 @@ class ConstRef(SymbolRef): def __init__(self, parent: SymbolID, default_value: Symbol): super().__init__(parent) - assert default_value.token in ("CONSTEXPR", "NUMBER", "CONST") + assert default_value.token in ("CONSTEXPR", "NUMBER", "CONST", "STRING") self._value = default_value @property @@ -26,7 +26,11 @@ def t(self) -> str: @property def value(self): - if self._value.token in ("NUMBER", "CONST"): + if self._value.token in ("NUMBER", "CONST", "STRING"): return self._value.value return self.t + + @property + def symbol(self) -> Symbol: + return self._value diff --git a/src/zxbc/zxbparser.py b/src/zxbc/zxbparser.py index 9e4fef48d..b1f9f11f9 100755 --- a/src/zxbc/zxbparser.py +++ b/src/zxbc/zxbparser.py @@ -38,6 +38,7 @@ is_number, is_numeric, is_static, + is_static_str, is_string, is_unsigned, ) @@ -365,8 +366,8 @@ def make_call(id_: str, lineno: int, args: sym.ARGLIST): A "call" is just an ID followed by a list of arguments. E.g. a(4) - a(4) can be a function call if 'a' is a function - - a(4) can be a string slice if a is a string variable: a$(4) - - a(4) can be an access to an array if a is an array + - a(4) can be a string slice if 'a' is a string variable: a$(4) + - a(4) can be an access to an array if 'a' is an array This function will inspect the id_. If it is undeclared then id_ will be taken as a forwarded function. @@ -705,8 +706,11 @@ def p_var_decl_ini(p): else: # keyword == "CONST" if defval is None: - errmsg.syntax_error_not_constant(p.lineno(4)) - return + if not is_static_str(value): + errmsg.syntax_error_not_constant(p.lineno(4)) + return + else: + defval = value SYMBOL_TABLE.declare_const(idlist[0].name, idlist[0].lineno, typedef, default_value=defval) diff --git a/tests/functional/zx48k/const_str.asm b/tests/functional/zx48k/const_str.asm new file mode 100644 index 000000000..473b4c70e --- /dev/null +++ b/tests/functional/zx48k/const_str.asm @@ -0,0 +1,1700 @@ + org 32768 +.core.__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (.core.__CALL_BACK__), hl + ei + call .core.__MEM_INIT + call .core.__PRINT_INIT + jp .core.__MAIN_PROGRAM__ +.core.__CALL_BACK__: + DEFW 0 +.core.ZXBASIC_USER_DATA: + ; Defines HEAP SIZE +.core.ZXBASIC_HEAP_SIZE EQU 4768 +.core.ZXBASIC_MEM_HEAP: + DEFS 4768 + ; Defines USER DATA Length in bytes +.core.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_END - .core.ZXBASIC_USER_DATA + .core.__LABEL__.ZXBASIC_USER_DATA_LEN EQU .core.ZXBASIC_USER_DATA_LEN + .core.__LABEL__.ZXBASIC_USER_DATA EQU .core.ZXBASIC_USER_DATA +_b: + DEFB 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + ld de, .LABEL.__LABEL0 + ld hl, _b + call .core.__STORE_STR + ld de, .LABEL.__LABEL0 + ld hl, _b + call .core.__STORE_STR + call .core.COPY_ATTR + ld hl, (_b) + xor a + call .core.__PRINTSTR + call .core.PRINT_EOL + 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 +.LABEL.__LABEL0: + DEFW 000Bh + DEFB 48h + DEFB 65h + DEFB 6Ch + DEFB 6Ch + DEFB 6Fh + DEFB 20h + DEFB 57h + DEFB 6Fh + DEFB 72h + DEFB 6Ch + DEFB 64h + ;; --- end of user code --- +#line 1 "/zxbasic/src/arch/zx48k/library-asm/copy_attr.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/sposn.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/sysvars.asm" + ;; ----------------------------------------------------------------------- + ;; ZX Basic System Vars + ;; Some of them will be mapped over Sinclair ROM ones for compatibility + ;; ----------------------------------------------------------------------- + push namespace core +SCREEN_ADDR: DW 16384 ; Screen address (can be pointed to other place to use a screen buffer) +SCREEN_ATTR_ADDR: DW 22528 ; Screen attribute address (ditto.) + ; These are mapped onto ZX Spectrum ROM VARS + CHARS EQU 23606 ; Pointer to ROM/RAM Charset + TVFLAGS EQU 23612 ; TV Flags + UDG EQU 23675 ; Pointer to UDG Charset + COORDS EQU 23677 ; Last PLOT coordinates + FLAGS2 EQU 23681 ; + ECHO_E EQU 23682 ; + DFCC EQU 23684 ; Next screen addr for PRINT + DFCCL EQU 23686 ; Next screen attr for PRINT + S_POSN EQU 23688 + ATTR_P EQU 23693 ; Current Permanent ATTRS set with INK, PAPER, etc commands + ATTR_T EQU 23695 ; temporary ATTRIBUTES + P_FLAG EQU 23697 ; + MEM0 EQU 23698 ; Temporary memory buffer used by ROM chars + SCR_COLS EQU 33 ; Screen with in columns + 1 + SCR_ROWS EQU 24 ; Screen height in rows + SCR_SIZE EQU (SCR_ROWS << 8) + SCR_COLS + pop namespace +#line 2 "/zxbasic/src/arch/zx48k/library-asm/sposn.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/attr.asm" + ; Attribute routines +; vim:ts=4:et:sw: +#line 1 "/zxbasic/src/arch/zx48k/library-asm/error.asm" + ; Simple error control routines +; vim:ts=4:et: + push namespace core + ERR_NR EQU 23610 ; Error code system variable + ; Error code definitions (as in ZX spectrum manual) +; Set error code with: + ; ld a, ERROR_CODE + ; ld (ERR_NR), a + ERROR_Ok EQU -1 + ERROR_SubscriptWrong EQU 2 + ERROR_OutOfMemory EQU 3 + ERROR_OutOfScreen EQU 4 + ERROR_NumberTooBig EQU 5 + ERROR_InvalidArg EQU 9 + ERROR_IntOutOfRange EQU 10 + ERROR_NonsenseInBasic EQU 11 + ERROR_InvalidFileName EQU 14 + ERROR_InvalidColour EQU 19 + ERROR_BreakIntoProgram EQU 20 + ERROR_TapeLoadingErr EQU 26 + ; Raises error using RST #8 +__ERROR: + ld (__ERROR_CODE), a + rst 8 +__ERROR_CODE: + nop + ret + ; Sets the error system variable, but keeps running. + ; Usually this instruction if followed by the END intermediate instruction. +__STOP: + ld (ERR_NR), a + ret + pop namespace +#line 6 "/zxbasic/src/arch/zx48k/library-asm/attr.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/print.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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 + LOCAL SET_SCR_ADDR + call __LOAD_S_POSN + jp __SET_SCR_PTR + ;; Receives HL = future value of S_POSN + ;; Stores it at (S_POSN) and refresh screen pointers (ATTR, SCR) +SET_SCR_ADDR: + ld (S_POSN), hl + ex de, hl + ld hl, SCR_SIZE + or a + sbc hl, de + ex de, hl + dec e + jp __SET_SCR_PTR +__PRINTCHAR: ; Print character store in accumulator (A register) + ; Modifies H'L', B'C', A'F', D'E', A + LOCAL PO_GR_1 + LOCAL __PRCHAR + LOCAL __PRINT_JUMP + LOCAL __SRCADDR + LOCAL __PRINT_UDG + LOCAL __PRGRAPH + LOCAL __PRINT_START + PRINT_JUMP_STATE EQU __PRINT_JUMP + 2 +__PRINT_JUMP: + exx ; Switch to alternative registers + jp __PRINT_START ; Where to jump. If we print 22 (AT), next two calls jumps to AT1 and AT2 respectively +__PRINT_START: +__PRINT_CHR: + cp ' ' + jr c, __PRINT_SPECIAL ; Characters below ' ' are special ones + ex af, af' ; Saves a value (char to print) for later + ld hl, (S_POSN) + dec l + jr nz, 1f + ld l, SCR_COLS - 1 + dec h + jr nz, 2f + inc h + push hl + call __SCROLL_SCR + pop hl +#line 92 "/zxbasic/src/arch/zx48k/library-asm/print.asm" +2: + call SET_SCR_ADDR + jr 4f +1: + ld (S_POSN), hl +4: + ex af, af' + cp 80h ; Is it a "normal" (printable) char + jr c, __SRCADDR + cp 90h ; Is it an UDG? + jr nc, __PRINT_UDG + ; Print an 8 bit pattern (80h to 8Fh) + ld b, a + call PO_GR_1 ; This ROM routine will generate the bit pattern at MEM0 + ld hl, MEM0 + jp __PRGRAPH + PO_GR_1 EQU 0B38h +__PRINT_UDG: + sub 90h ; Sub ASC code + ld bc, (UDG) + jr __PRGRAPH0 + __SOURCEADDR EQU (__SRCADDR + 1) ; Address of the pointer to chars source +__SRCADDR: + ld bc, (CHARS) +__PRGRAPH0: + add a, a ; A = a * 2 (since a < 80h) ; Thanks to Metalbrain at http://foro.speccy.org + ld l, a + ld h, 0 ; HL = a * 2 (accumulator) + add hl, hl + add hl, hl ; HL = a * 8 + add hl, bc ; HL = CHARS address +__PRGRAPH: + ex de, hl ; HL = Write Address, DE = CHARS address + bit 2, (iy + $47) + call nz, __BOLD +#line 139 "/zxbasic/src/arch/zx48k/library-asm/print.asm" + bit 4, (iy + $47) + call nz, __ITALIC +#line 144 "/zxbasic/src/arch/zx48k/library-asm/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 209 "/zxbasic/src/arch/zx48k/library-asm/print.asm" +1: + ld l, 1 +__PRINT_EOL_END: + call SET_SCR_ADDR + exx + ret +__PRINT_COM: + exx + push hl + push de + push bc + call PRINT_COMMA + pop bc + pop de + pop hl + ret +__PRINT_TAB: + ld hl, __PRINT_TAB1 + jr __PRINT_SET_STATE +__PRINT_TAB1: + ld (MEM0), a + ld hl, __PRINT_TAB2 + jr __PRINT_SET_STATE +__PRINT_TAB2: + ld a, (MEM0) ; Load tab code (ignore the current one) + ld hl, __PRINT_START + ld (PRINT_JUMP_STATE), hl + exx + push hl + push bc + push de + call PRINT_TAB + pop de + pop bc + pop hl + ret +__PRINT_AT: + ld hl, __PRINT_AT1 + jr __PRINT_SET_STATE +__PRINT_NOP: +__PRINT_RESTART: + ld hl, __PRINT_START +__PRINT_SET_STATE: + ld (PRINT_JUMP_STATE), hl ; Saves next entry call + exx + ret +__PRINT_AT1: ; Jumps here if waiting for 1st parameter + ld hl, (S_POSN) + ld h, a + ld a, SCR_ROWS + sub h + ld (S_POSN + 1), a + ld hl, __PRINT_AT2 + jr __PRINT_SET_STATE +__PRINT_AT2: + call __LOAD_S_POSN + ld e, a + call __SAVE_S_POSN + jr __PRINT_RESTART +__PRINT_DEL: + call __LOAD_S_POSN ; Gets current screen position + dec e + ld a, -1 + cp e + jr nz, 3f + ld e, SCR_COLS - 2 + dec d + cp d + jr nz, 3f + ld d, SCR_ROWS - 1 +3: + call __SAVE_S_POSN + exx + ret +__PRINT_INK: + ld hl, __PRINT_INK2 + jr __PRINT_SET_STATE +__PRINT_INK2: + call INK_TMP + jr __PRINT_RESTART +__PRINT_PAP: + ld hl, __PRINT_PAP2 + jr __PRINT_SET_STATE +__PRINT_PAP2: + call PAPER_TMP + jr __PRINT_RESTART +__PRINT_FLA: + ld hl, __PRINT_FLA2 + jr __PRINT_SET_STATE +__PRINT_FLA2: + call FLASH_TMP + jr __PRINT_RESTART +__PRINT_BRI: + ld hl, __PRINT_BRI2 + jr __PRINT_SET_STATE +__PRINT_BRI2: + call BRIGHT_TMP + jr __PRINT_RESTART +__PRINT_INV: + ld hl, __PRINT_INV2 + jr __PRINT_SET_STATE +__PRINT_INV2: + call INVERSE_TMP + jr __PRINT_RESTART +__PRINT_OVR: + ld hl, __PRINT_OVR2 + jr __PRINT_SET_STATE +__PRINT_OVR2: + call OVER_TMP + jr __PRINT_RESTART +__PRINT_BOLD: + ld hl, __PRINT_BOLD2 + jp __PRINT_SET_STATE +__PRINT_BOLD2: + call BOLD_TMP + jp __PRINT_RESTART +#line 353 "/zxbasic/src/arch/zx48k/library-asm/print.asm" +__PRINT_ITA: + ld hl, __PRINT_ITA2 + jp __PRINT_SET_STATE +__PRINT_ITA2: + call ITALIC_TMP + jp __PRINT_RESTART +#line 363 "/zxbasic/src/arch/zx48k/library-asm/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 384 "/zxbasic/src/arch/zx48k/library-asm/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 412 "/zxbasic/src/arch/zx48k/library-asm/print.asm" + LOCAL __SCROLL_SCR +#line 486 "/zxbasic/src/arch/zx48k/library-asm/print.asm" + __SCROLL_SCR EQU 0DFEh ; Use ROM SCROLL +#line 488 "/zxbasic/src/arch/zx48k/library-asm/print.asm" +#line 489 "/zxbasic/src/arch/zx48k/library-asm/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 545 "/zxbasic/src/arch/zx48k/library-asm/print.asm" + LOCAL __PRINT_BOLD2 +#line 551 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/copy_attr.asm" +#line 4 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/copy_attr.asm" +__REFRESH_TMP: + ld a, (hl) + and 0b10101010 + ld c, a + rra + or c + ld (hl), a + ret + ENDP + pop namespace +#line 41 "zx48k/const_str.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/printstr.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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/arch/zx48k/library-asm/free.asm" + ; --------------------------------------------------------------------- + ; MEM_FREE + ; Frees a block of memory + ; +; Parameters: + ; HL = Pointer to the block to be freed. If HL is NULL (0) nothing + ; is done + ; --------------------------------------------------------------------- + push namespace core +MEM_FREE: +__MEM_FREE: ; Frees the block pointed by HL + ; HL DE BC & AF modified + PROC + LOCAL __MEM_LOOP2 + LOCAL __MEM_LINK_PREV + LOCAL __MEM_JOIN_TEST + LOCAL __MEM_BLOCK_JOIN + ld a, h + or l + ret z ; Return if NULL pointer + dec hl + dec hl + ld b, h + ld c, l ; BC = Block pointer + ld hl, ZXBASIC_MEM_HEAP ; This label point to the heap start +__MEM_LOOP2: + inc hl + inc hl ; Next block ptr + ld e, (hl) + inc hl + ld d, (hl) ; Block next ptr + ex de, hl ; DE = &(block->next); HL = block->next + ld a, h ; HL == NULL? + or l + jp z, __MEM_LINK_PREV; if so, link with previous + or a ; Clear carry flag + sbc hl, bc ; Carry if BC > HL => This block if before + add hl, bc ; Restores HL, preserving Carry flag + jp c, __MEM_LOOP2 ; This block is before. Keep searching PASS the block + ;------ At this point current HL is PAST BC, so we must link (DE) with BC, and HL in BC->next +__MEM_LINK_PREV: ; Link (DE) with BC, and BC->next with HL + ex de, hl + push hl + dec hl + ld (hl), c + inc hl + ld (hl), b ; (DE) <- BC + ld h, b ; HL <- BC (Free block ptr) + ld l, c + inc hl ; Skip block length (2 bytes) + inc hl + ld (hl), e ; Block->next = DE + inc hl + ld (hl), d + ; --- LINKED ; HL = &(BC->next) + 2 + call __MEM_JOIN_TEST + pop hl +__MEM_JOIN_TEST: ; Checks for fragmented contiguous blocks and joins them + ; hl = Ptr to current block + 2 + ld d, (hl) + dec hl + ld e, (hl) + dec hl + ld b, (hl) ; Loads block length into BC + dec hl + ld c, (hl) ; + push hl ; Saves it for later + add hl, bc ; Adds its length. If HL == DE now, it must be joined + or a + sbc hl, de ; If Z, then HL == DE => We must join + pop hl + ret nz +__MEM_BLOCK_JOIN: ; Joins current block (pointed by HL) with next one (pointed by DE). HL->length already in BC + push hl ; Saves it for later + ex de, hl + ld e, (hl) ; DE -> block->next->length + inc hl + ld d, (hl) + inc hl + ex de, hl ; DE = &(block->next) + add hl, bc ; HL = Total Length + ld b, h + ld c, l ; BC = Total Length + ex de, hl + ld e, (hl) + inc hl + ld d, (hl) ; DE = block->next + pop hl ; Recovers Pointer to block + ld (hl), c + inc hl + ld (hl), b ; Length Saved + inc hl + ld (hl), e + inc hl + ld (hl), d ; Next saved + ret + ENDP + pop namespace +#line 5 "/zxbasic/src/arch/zx48k/library-asm/printstr.asm" + ; PRINT command routine + ; Prints string pointed by HL + push namespace core +PRINT_STR: +__PRINTSTR: ; __FASTCALL__ Entry to print_string + PROC + LOCAL __PRINT_STR_LOOP + LOCAL __PRINT_STR_END + ld d, a ; Saves A reg (Flag) for later + ld a, h + or l + ret z ; Return if the pointer is NULL + push hl + ld c, (hl) + inc hl + ld b, (hl) + inc hl ; BC = LEN(a$); HL = &a$ +__PRINT_STR_LOOP: + ld a, b + or c + jr z, __PRINT_STR_END ; END if BC (counter = 0) + ld a, (hl) + call __PRINTCHAR + inc hl + dec bc + jp __PRINT_STR_LOOP +__PRINT_STR_END: + pop hl + ld a, d ; Recovers A flag + or a ; If not 0 this is a temporary string. Free it + ret z + jp __MEM_FREE ; Frees str from heap and return from there +__PRINT_STR: + ; Fastcall Entry + ; It ONLY prints strings + ; HL = String start + ; BC = String length (Number of chars) + push hl ; Push str address for later + ld d, a ; Saves a FLAG + jp __PRINT_STR_LOOP + ENDP + pop namespace +#line 43 "zx48k/const_str.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/strcpy.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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/arch/zx48k/library-asm/alloc.asm" + ret z ; NULL +#line 115 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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/arch/zx48k/library-asm/storestr.asm" + push namespace core +__PISTORE_STR: ; Indirect assignement at (IX + BC) + push ix + pop hl + add hl, bc +__ISTORE_STR: ; Indirect assignement, 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 ; Array pointer to variable memory address + 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 elemem ptr + pop hl ; Returns ptr to b$ in HL (Caller might needed to free it from memory) + ret + pop namespace +#line 44 "zx48k/const_str.bas" + END diff --git a/tests/functional/zx48k/const_str.bas b/tests/functional/zx48k/const_str.bas new file mode 100644 index 000000000..b16811ccc --- /dev/null +++ b/tests/functional/zx48k/const_str.bas @@ -0,0 +1,5 @@ +CONST a = "Hello World" + +LET b = a +LET b = "Hello World" +PRINT b