diff --git a/src/arch/z80/backend/_float.py b/src/arch/z80/backend/_float.py index 3e891226b..05e44e6f5 100644 --- a/src/arch/z80/backend/_float.py +++ b/src/arch/z80/backend/_float.py @@ -49,7 +49,7 @@ def _float_oper(op1, op2=None) -> list[str]: """Returns pop sequence for floating point operands 1st operand in A DE BC, 2nd operand remains in the stack - Unlike 8bit and 16bit version, this does not supports + Unlike 8bit and 16bit version, this does not support operands inversion. Since many of the instructions are implemented as functions, they must support this. @@ -83,7 +83,7 @@ def _float_oper(op1, op2=None) -> list[str]: else: output.append("pop hl") - output.append(runtime_call(RuntimeLabel.ILOADF)) + output.append(runtime_call(RuntimeLabel.LOADF)) else: if op[0] == "_": output.append("ld a, (%s)" % op) @@ -94,7 +94,7 @@ def _float_oper(op1, op2=None) -> list[str]: if op2 is not None: op = op1 - if is_float(op): # An float must be in the stack. Let's push it + if is_float(op): # A float must be in the stack. Let's push it A, DE, BC = _float(op) output.append("ld hl, %s" % BC) output.append("push hl") diff --git a/src/arch/z80/backend/_parray.py b/src/arch/z80/backend/_parray.py index 394091a12..5ed7a7ebb 100644 --- a/src/arch/z80/backend/_parray.py +++ b/src/arch/z80/backend/_parray.py @@ -102,12 +102,10 @@ def _paload32(ins: Quad) -> list[str]: def _paloadf(ins: Quad) -> list[str]: - """Loads a floating point value from a memory address. - If 2nd arg. start with '*', it is always treated as - an indirect value. - """ + """Loads a floating point value from a memory address.""" output = _paddr(ins[2]) - output.append(runtime_call(RuntimeLabel.ILOADF)) + + output.append(runtime_call(RuntimeLabel.LOADF)) output.extend(_fpush()) return output diff --git a/src/arch/z80/visitor/translator.py b/src/arch/z80/visitor/translator.py index 98bd60d34..ecf7b7aa5 100644 --- a/src/arch/z80/visitor/translator.py +++ b/src/arch/z80/visitor/translator.py @@ -240,9 +240,9 @@ def visit_ARRAYLOAD(self, node): t1 = optemps.new_t() t2 = optemps.new_t() t3 = optemps.new_t() - self.ic_pload(gl.PTR_TYPE, t1, -(node.entry.offset - self.TYPE(gl.PTR_TYPE).size)) - self.ic_add(gl.PTR_TYPE, t2, t1, node.offset) - self.ic_load(node.type_, t3, "*$%s" % t2) + self.ic_pload(gl.PTR_TYPE, t1, -node.entry.offset + self.TYPE(gl.PTR_TYPE).size) + self.ic_add(gl.PTR_TYPE, t2, t1, offset) + self.ic_load(node.type_, t3, f"*${t2}") def _emit_arraycopy_child(self, child: symbols.ID): assert child.token == "VARARRAY" diff --git a/tests/functional/zx48k/local_float_array0.asm b/tests/functional/zx48k/local_float_array0.asm new file mode 100644 index 000000000..87debf48c --- /dev/null +++ b/tests/functional/zx48k/local_float_array0.asm @@ -0,0 +1,880 @@ + 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 + 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 +_f: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test + ld hl, _f + call .core.__STOREF + 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 +_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, 20 + call .core.__ALLOC_INITIALIZED_LOCAL_ARRAY + push ix + pop hl + ld bc, -2 + add hl, bc + ex de, hl + ld hl, .LABEL.__LABEL2 + ld bc, 2 + ldir + ld l, (ix-2) + ld h, (ix-1) + push hl + push ix + pop hl + ld de, -6 + add hl, de + call .core.__ARRAY + call .core.__LOADF +_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/arch/zx48k/library-asm/array.asm" +; vim: ts=4:et:sw=4: + ; Copyleft (K) by Jose M. Rodriguez de la Rosa + ; (a.k.a. Boriel) +; http://www.boriel.com + ; ------------------------------------------------------------------- + ; Simple array Index routine + ; Number of total indexes dimensions - 1 at beginning of memory + ; HL = Start of array memory (First two bytes contains N-1 dimensions) + ; Dimension values on the stack, (top of the stack, highest dimension) + ; E.g. A(2, 4) -> PUSH <4>; PUSH <2> + ; For any array of N dimension A(aN-1, ..., a1, a0) + ; and dimensions D[bN-1, ..., b1, b0], the offset is calculated as + ; O = [a0 + b0 * (a1 + b1 * (a2 + ... bN-2(aN-1)))] +; What I will do here is to calculate the following sequence: + ; ((aN-1 * bN-2) + aN-2) * bN-3 + ... +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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 20 "/zxbasic/src/arch/zx48k/library-asm/array.asm" +#line 24 "/zxbasic/src/arch/zx48k/library-asm/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 +__ARRAY: + PROC + LOCAL LOOP + LOCAL ARRAY_END + LOCAL RET_ADDRESS ; Stores return address + LOCAL TMP_ARR_PTR ; Stores pointer temporarily + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld (TMP_ARR_PTR), hl + ex de, hl + ex (sp), hl ; Return address in HL, array address in the stack + ld (RET_ADDRESS + 1), hl ; Stores it for later + exx + pop hl ; Will use H'L' as the pointer + ld c, (hl) ; Loads Number of dimensions from (hl) + inc hl + ld b, (hl) + inc hl ; Ready + exx + ld hl, 0 ; HL = Offset "accumulator" +LOOP: +#line 64 "/zxbasic/src/arch/zx48k/library-asm/array.asm" + pop bc ; Get next index (Ai) from the stack +#line 74 "/zxbasic/src/arch/zx48k/library-asm/array.asm" + add hl, bc ; 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 + ld e, (hl) ; Loads next dimension into D'E' + inc hl + ld d, (hl) + inc hl + push de + dec bc ; Decrements loop counter + exx + pop de ; DE = Max bound Number (i-th dimension) + call __FNMUL + jp LOOP +ARRAY_END: + ld a, (hl) + exx +#line 103 "/zxbasic/src/arch/zx48k/library-asm/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 113 "/zxbasic/src/arch/zx48k/library-asm/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 +RET_ADDRESS: + jp 0 + ;; Performs a faster multiply for little 16bit numbs + LOCAL __FNMUL, __FNMUL2 +__FNMUL: + xor a + or h + jp nz, __MUL16_FAST + or l + ret z + cp 33 + jp nc, __MUL16_FAST + ld b, l + ld l, h ; HL = 0 +__FNMUL2: + add hl, de + djnz __FNMUL2 + ret +TMP_ARR_PTR: + DW 0 ; temporary storage for pointer to tables + ENDP + pop namespace +#line 62 "zx48k/local_float_array0.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/arrayalloc.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/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. +#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 69 "/zxbasic/src/arch/zx48k/library-asm/alloc.asm" +#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 70 "/zxbasic/src/arch/zx48k/library-asm/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/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 13 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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 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 + ; [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 139 "/zxbasic/src/arch/zx48k/library-asm/arrayalloc.asm" + pop namespace +#line 63 "zx48k/local_float_array0.bas" +#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. + ; --------------------------------------------------------------------- + ; 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 64 "zx48k/local_float_array0.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/iloadf.asm" + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + push namespace core +__ILOADF: + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- (HL) +__LOADF: ; Loads a 40 bits FP number from address pointed by HL + ld a, (hl) + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ret + pop namespace +#line 65 "zx48k/local_float_array0.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 66 "zx48k/local_float_array0.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 05h +.LABEL.__LABEL1: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 81h + DEFB 40h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 82h + DEFB 20h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 82h + DEFB 60h + DEFB 00h + DEFB 00h + DEFB 00h +.LABEL.__LABEL2: + DEFB 01h + DEFB 00h + END diff --git a/tests/functional/zx48k/local_float_array0.bas b/tests/functional/zx48k/local_float_array0.bas new file mode 100644 index 000000000..d18573a9c --- /dev/null +++ b/tests/functional/zx48k/local_float_array0.bas @@ -0,0 +1,10 @@ +REM Test reading a float from a local array + +FUNCTION test() as Float + DIM x(3) as Float = {0.0, 1.5, 2.5, 3.5} + DIM i as UInteger = 1 + RETURN x(i) +END FUNCTION + +DIM f As Float +LET f = test diff --git a/tests/functional/zx48k/local_float_array1.asm b/tests/functional/zx48k/local_float_array1.asm new file mode 100644 index 000000000..4b27d364b --- /dev/null +++ b/tests/functional/zx48k/local_float_array1.asm @@ -0,0 +1,731 @@ + 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 + 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 +_f: + DEFB 00, 00, 00, 00, 00 +.core.ZXBASIC_USER_DATA_END: +.core.__MAIN_PROGRAM__: + call _test + ld hl, _f + call .core.__STOREF + 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 +_test: + push ix + ld ix, 0 + add ix, sp + ld hl, 0 + push hl + push hl + ld hl, .LABEL.__LABEL1 + push hl + ld hl, -4 + ld de, .LABEL.__LABEL0 + ld bc, 20 + call .core.__ALLOC_INITIALIZED_LOCAL_ARRAY + ld l, (ix-2) + ld h, (ix-1) + ld de, 5 + add hl, de + call .core.__LOADF +_test__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 + ret + ;; --- end of user code --- +#line 1 "/zxbasic/src/arch/zx48k/library-asm/arrayalloc.asm" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/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/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. +#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 69 "/zxbasic/src/arch/zx48k/library-asm/alloc.asm" +#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 70 "/zxbasic/src/arch/zx48k/library-asm/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/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 13 "/zxbasic/src/arch/zx48k/library-asm/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/arch/zx48k/library-asm/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 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 + ; [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 139 "/zxbasic/src/arch/zx48k/library-asm/arrayalloc.asm" + pop namespace +#line 49 "zx48k/local_float_array1.bas" +#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. + ; --------------------------------------------------------------------- + ; 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 50 "zx48k/local_float_array1.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/iloadf.asm" + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- ((HL)) + push namespace core +__ILOADF: + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + ; __FASTCALL__ routine which + ; loads a 40 bits floating point into A ED CB + ; stored at position pointed by POINTER HL + ;A DE, BC <-- (HL) +__LOADF: ; Loads a 40 bits FP number from address pointed by HL + ld a, (hl) + inc hl + ld e, (hl) + inc hl + ld d, (hl) + inc hl + ld c, (hl) + inc hl + ld b, (hl) + ret + pop namespace +#line 51 "zx48k/local_float_array1.bas" +#line 1 "/zxbasic/src/arch/zx48k/library-asm/storef.asm" + push namespace core +__PISTOREF: ; Indect Stores a float (A, E, D, C, B) at location stored in memory, pointed by (IX + HL) + push de + ex de, hl ; DE <- HL + push ix + pop hl ; HL <- IX + add hl, de ; HL <- IX + HL + pop de +__ISTOREF: ; Load address at hl, and stores A,E,D,C,B registers at that address. Modifies A' register + ex af, af' + ld a, (hl) + inc hl + ld h, (hl) + ld l, a ; HL = (HL) + ex af, af' +__STOREF: ; Stores the given FP number in A EDCB at address HL + ld (hl), a + inc hl + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c + inc hl + ld (hl), b + ret + pop namespace +#line 52 "zx48k/local_float_array1.bas" +.LABEL.__LABEL0: + DEFB 00h + DEFB 00h + DEFB 05h +.LABEL.__LABEL1: + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 81h + DEFB 40h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 82h + DEFB 20h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 82h + DEFB 60h + DEFB 00h + DEFB 00h + DEFB 00h + END diff --git a/tests/functional/zx48k/local_float_array1.bas b/tests/functional/zx48k/local_float_array1.bas new file mode 100644 index 000000000..71c405a47 --- /dev/null +++ b/tests/functional/zx48k/local_float_array1.bas @@ -0,0 +1,9 @@ +REM Test reading a float from a local array + +FUNCTION test() as Float + DIM x(3) as Float = {0.0, 1.5, 2.5, 3.5} + RETURN x(1) +END FUNCTION + +DIM f As Float +LET f = test