mirror of
https://github.com/irmen/prog8.git
synced 2024-10-10 10:23:47 +00:00
zp variable allocation
This commit is contained in:
parent
e6804b2bf9
commit
62dfdace71
@ -9,13 +9,18 @@ import re
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import linecache
|
import linecache
|
||||||
from typing import Optional, Tuple, Set, Dict, Any, no_type_check
|
from typing import Optional, Tuple, Set, Dict, List, Any, no_type_check
|
||||||
import attr
|
import attr
|
||||||
from .plyparse import parse_file, ParseError, Module, Directive, Block, Subroutine, Scope, VarDef, LiteralValue, \
|
from .plyparse import parse_file, ParseError, Module, Directive, Block, Subroutine, Scope, VarDef, LiteralValue, \
|
||||||
SubCall, Goto, Return, Assignment, InlineAssembly, Register, Expression, ProgramFormat, ZpOptions,\
|
SubCall, Goto, Return, Assignment, InlineAssembly, Register, Expression, ProgramFormat, ZpOptions,\
|
||||||
SymbolName, Dereference, AddressOf
|
SymbolName, Dereference, AddressOf
|
||||||
from .plylex import SourceRef, print_bold
|
from .plylex import SourceRef, print_bold
|
||||||
from .optimize import optimize
|
from .optimize import optimize
|
||||||
|
from .datatypes import DataType, datatype_sizes
|
||||||
|
|
||||||
|
|
||||||
|
class CompileError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PlyParser:
|
class PlyParser:
|
||||||
@ -37,6 +42,8 @@ class PlyParser:
|
|||||||
# these shall only be done on the main module after all imports have been done:
|
# these shall only be done on the main module after all imports have been done:
|
||||||
self.apply_directive_options(module)
|
self.apply_directive_options(module)
|
||||||
self.determine_subroutine_usage(module)
|
self.determine_subroutine_usage(module)
|
||||||
|
# XXX merge zero page from imported modules??? do we still have to do that?
|
||||||
|
self.allocate_zeropage_vars(module)
|
||||||
except ParseError as x:
|
except ParseError as x:
|
||||||
self.handle_parse_error(x)
|
self.handle_parse_error(x)
|
||||||
if self.parse_errors:
|
if self.parse_errors:
|
||||||
@ -60,7 +67,6 @@ class PlyParser:
|
|||||||
zeropage.scope.add_node(node, 0)
|
zeropage.scope.add_node(node, 0)
|
||||||
elif isinstance(node, VarDef):
|
elif isinstance(node, VarDef):
|
||||||
zeropage.scope.add_node(node)
|
zeropage.scope.add_node(node)
|
||||||
print("ADDED ZP VAR", node) # XXX
|
|
||||||
else:
|
else:
|
||||||
raise ParseError("only variables and directives allowed in zeropage block", node.sourceref)
|
raise ParseError("only variables and directives allowed in zeropage block", node.sourceref)
|
||||||
else:
|
else:
|
||||||
@ -70,6 +76,19 @@ class PlyParser:
|
|||||||
# add the zero page again, as the very first block
|
# add the zero page again, as the very first block
|
||||||
module.scope.add_node(zeropage, 0)
|
module.scope.add_node(zeropage, 0)
|
||||||
|
|
||||||
|
def allocate_zeropage_vars(self, module: Module) -> None:
|
||||||
|
# allocate zeropage variables to the available free zp addresses
|
||||||
|
if not module.scope.nodes:
|
||||||
|
return
|
||||||
|
zpnode = module.scope.nodes[0]
|
||||||
|
assert zpnode.name == "ZP", "first node should be the (only) ZP"
|
||||||
|
zeropage = Zeropage(module.zp_options)
|
||||||
|
for vardef in zpnode.scope.filter_nodes(VarDef):
|
||||||
|
try:
|
||||||
|
vardef.zp_address = zeropage.allocate(vardef.name, vardef.datatype)
|
||||||
|
except CompileError as x:
|
||||||
|
raise ParseError(str(x), vardef.sourceref)
|
||||||
|
|
||||||
@no_type_check
|
@no_type_check
|
||||||
def process_all_expressions(self, module: Module) -> None:
|
def process_all_expressions(self, module: Module) -> None:
|
||||||
# process/simplify all expressions (constant folding etc)
|
# process/simplify all expressions (constant folding etc)
|
||||||
@ -383,6 +402,60 @@ class PlyParser:
|
|||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
|
class Zeropage:
|
||||||
|
SCRATCH_B1 = 0x02
|
||||||
|
SCRATCH_B2 = 0x03
|
||||||
|
SCRATCH_W1 = 0xfb # $fb/$fc
|
||||||
|
SCRATCH_W2 = 0xfd # $fd/$fe
|
||||||
|
|
||||||
|
def __init__(self, options: ZpOptions) -> None:
|
||||||
|
self.free = [] # type: List[int]
|
||||||
|
self.allocations = {} # type: Dict[int, Tuple[str, DataType]]
|
||||||
|
if options in (ZpOptions.CLOBBER_RESTORE, ZpOptions.CLOBBER):
|
||||||
|
# clobber the zp, more free storage, yay!
|
||||||
|
self.free = list(range(0x04, 0xfb)) + [0xff]
|
||||||
|
for updated_by_irq in [0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6]:
|
||||||
|
self.free.remove(updated_by_irq)
|
||||||
|
else:
|
||||||
|
# these are valid for the C-64 (when no RS232 I/O is performed):
|
||||||
|
# ($02, $03, $fb-$fc, $fd-$fe are reserved as scratch addresses for various routines)
|
||||||
|
self.free = [0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa]
|
||||||
|
assert self.SCRATCH_B1 not in self.free
|
||||||
|
assert self.SCRATCH_B2 not in self.free
|
||||||
|
assert self.SCRATCH_W1 not in self.free
|
||||||
|
assert self.SCRATCH_W2 not in self.free
|
||||||
|
|
||||||
|
def allocate(self, name: str, datatype: DataType) -> int:
|
||||||
|
assert not name or name not in {a[0] for a in self.allocations.values()}, "var name is not unique"
|
||||||
|
|
||||||
|
def sequential_free(location: int) -> bool:
|
||||||
|
return all(location + i in self.free for i in range(size))
|
||||||
|
|
||||||
|
def lone_byte(location: int) -> bool:
|
||||||
|
return (location-1) not in self.free and (location+1) not in self.free and location in self.free
|
||||||
|
|
||||||
|
def make_allocation(location: int) -> int:
|
||||||
|
for loc in range(location, location + size):
|
||||||
|
self.free.remove(loc)
|
||||||
|
self.allocations[location] = (name or "<unnamed>", datatype)
|
||||||
|
return location
|
||||||
|
|
||||||
|
size = datatype_sizes[datatype]
|
||||||
|
if len(self.free) > 0:
|
||||||
|
if size == 1:
|
||||||
|
for candidate in range(min(self.free), max(self.free)+1):
|
||||||
|
if lone_byte(candidate):
|
||||||
|
return make_allocation(candidate)
|
||||||
|
return make_allocation(self.free[0])
|
||||||
|
for candidate in range(min(self.free), max(self.free)+1):
|
||||||
|
if sequential_free(candidate):
|
||||||
|
return make_allocation(candidate)
|
||||||
|
raise CompileError("ERROR: no more free space in ZP to allocate {:d} sequential bytes".format(size))
|
||||||
|
|
||||||
|
def available(self) -> int:
|
||||||
|
return len(self.free)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
description = "Compiler for IL65 language, code name 'Sick'"
|
description = "Compiler for IL65 language, code name 'Sick'"
|
||||||
print("\n" + description + "\n")
|
print("\n" + description + "\n")
|
||||||
|
@ -47,6 +47,13 @@ class DataType(enum.Enum):
|
|||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
datatype_sizes = {
|
||||||
|
DataType.BYTE: 1,
|
||||||
|
DataType.WORD: 2,
|
||||||
|
DataType.FLOAT: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS}
|
STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS}
|
||||||
|
|
||||||
REGISTER_SYMBOLS = {"A", "X", "Y", "AX", "AY", "XY", "SC", "SI"}
|
REGISTER_SYMBOLS = {"A", "X", "Y", "AX", "AY", "XY", "SC", "SI"}
|
||||||
|
@ -13,7 +13,7 @@ from typing import Dict, TextIO, List, Any
|
|||||||
from .plylex import print_bold
|
from .plylex import print_bold
|
||||||
from .plyparse import Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions, \
|
from .plyparse import Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions, \
|
||||||
InlineAssembly, Return, Register, LiteralValue
|
InlineAssembly, Return, Register, LiteralValue
|
||||||
from .datatypes import VarType, DataType, to_hex, mflpt5_to_float, to_mflpt5, STRING_DATATYPES
|
from .datatypes import VarType, DataType, datatype_sizes, to_hex, mflpt5_to_float, to_mflpt5, STRING_DATATYPES
|
||||||
|
|
||||||
|
|
||||||
class CodeError(Exception):
|
class CodeError(Exception):
|
||||||
@ -126,7 +126,7 @@ class AssemblyGenerator:
|
|||||||
self.p("; file: '{:s}' src l. {:d}\n".format(zpblock.sourceref.file, zpblock.sourceref.line))
|
self.p("; file: '{:s}' src l. {:d}\n".format(zpblock.sourceref.file, zpblock.sourceref.line))
|
||||||
self.p("{:s}\t.proc\n".format(zpblock.label))
|
self.p("{:s}\t.proc\n".format(zpblock.label))
|
||||||
self.generate_block_init(zpblock)
|
self.generate_block_init(zpblock)
|
||||||
self.generate_block_vars(zpblock)
|
self.generate_block_vars(zpblock, True)
|
||||||
# there's no code in the zero page block.
|
# there's no code in the zero page block.
|
||||||
self.p("\v.pend\n")
|
self.p("\v.pend\n")
|
||||||
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
||||||
@ -270,7 +270,7 @@ class AssemblyGenerator:
|
|||||||
self.p("_init_strings_size = * - _init_strings_start")
|
self.p("_init_strings_size = * - _init_strings_start")
|
||||||
self.p("")
|
self.p("")
|
||||||
|
|
||||||
def generate_block_vars(self, block: Block) -> None:
|
def generate_block_vars(self, block: Block, zeropage: bool=False) -> None:
|
||||||
# Generate the block variable storage.
|
# Generate the block variable storage.
|
||||||
# The memory bytes of the allocated variables is set to zero (so it compresses very well),
|
# The memory bytes of the allocated variables is set to zero (so it compresses very well),
|
||||||
# their actual starting values are set by the block init code.
|
# their actual starting values are set by the block init code.
|
||||||
@ -307,39 +307,46 @@ class AssemblyGenerator:
|
|||||||
else:
|
else:
|
||||||
raise CodeError("invalid var type")
|
raise CodeError("invalid var type")
|
||||||
self.p("; normal variables - initial values will be set by init code")
|
self.p("; normal variables - initial values will be set by init code")
|
||||||
string_vars = []
|
if zeropage:
|
||||||
for vardef in vars_by_vartype.get(VarType.VAR, []):
|
# zeropage uses the zp_address we've allocated, instead of allocating memory here
|
||||||
# create a definition for a variable that takes up empty space and will be initialized at startup
|
for vardef in vars_by_vartype.get(VarType.VAR, []):
|
||||||
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
assert vardef.zp_address is not None
|
||||||
assert vardef.size == [1]
|
self.p("\v{:s} = {:s}\t; {:s} ({:d})".format(vardef.name, to_hex(vardef.zp_address),
|
||||||
if vardef.datatype == DataType.BYTE:
|
vardef.datatype.name.lower(), datatype_sizes[vardef.datatype]))
|
||||||
self.p("{:s}\v.byte ?".format(vardef.name))
|
else:
|
||||||
elif vardef.datatype == DataType.WORD:
|
# create definitions for the variables that takes up empty space and will be initialized at startup
|
||||||
self.p("{:s}\v.word ?".format(vardef.name))
|
string_vars = []
|
||||||
elif vardef.datatype == DataType.FLOAT:
|
for vardef in vars_by_vartype.get(VarType.VAR, []):
|
||||||
self.p("{:s}\v.fill 5\t\t; float".format(vardef.name))
|
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
assert vardef.size == [1]
|
||||||
|
if vardef.datatype == DataType.BYTE:
|
||||||
|
self.p("{:s}\v.byte ?".format(vardef.name))
|
||||||
|
elif vardef.datatype == DataType.WORD:
|
||||||
|
self.p("{:s}\v.word ?".format(vardef.name))
|
||||||
|
elif vardef.datatype == DataType.FLOAT:
|
||||||
|
self.p("{:s}\v.fill 5\t\t; float".format(vardef.name))
|
||||||
|
else:
|
||||||
|
raise CodeError("weird datatype")
|
||||||
|
elif vardef.datatype in (DataType.BYTEARRAY, DataType.WORDARRAY):
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
if vardef.datatype == DataType.BYTEARRAY:
|
||||||
|
self.p("{:s}\v.fill {:d}\t\t; bytearray".format(vardef.name, vardef.size[0]))
|
||||||
|
elif vardef.datatype == DataType.WORDARRAY:
|
||||||
|
self.p("{:s}\v.fill {:d}*2\t\t; wordarray".format(vardef.name, vardef.size[0]))
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid datatype", vardef.datatype)
|
||||||
|
elif vardef.datatype == DataType.MATRIX:
|
||||||
|
assert len(vardef.size) == 2
|
||||||
|
self.p("{:s}\v.fill {:d}\t\t; matrix {:d}*{:d} bytes"
|
||||||
|
.format(vardef.name, vardef.size[0] * vardef.size[1], vardef.size[0], vardef.size[1]))
|
||||||
|
elif vardef.datatype in STRING_DATATYPES:
|
||||||
|
string_vars.append(vardef)
|
||||||
else:
|
else:
|
||||||
raise CodeError("weird datatype")
|
raise CodeError("unknown variable type " + str(vardef.datatype))
|
||||||
elif vardef.datatype in (DataType.BYTEARRAY, DataType.WORDARRAY):
|
if string_vars:
|
||||||
assert len(vardef.size) == 1
|
self.p("il65_string_vars_start")
|
||||||
if vardef.datatype == DataType.BYTEARRAY:
|
for svar in sorted(string_vars, key=lambda v: v.name): # must be the same order as in the init routine!!!
|
||||||
self.p("{:s}\v.fill {:d}\t\t; bytearray".format(vardef.name, vardef.size[0]))
|
self.p("{:s}\v.fill {:d}+1\t\t; {}".format(svar.name, len(svar.value), svar.datatype.name.lower()))
|
||||||
elif vardef.datatype == DataType.WORDARRAY:
|
|
||||||
self.p("{:s}\v.fill {:d}*2\t\t; wordarray".format(vardef.name, vardef.size[0]))
|
|
||||||
else:
|
|
||||||
raise CodeError("invalid datatype", vardef.datatype)
|
|
||||||
elif vardef.datatype == DataType.MATRIX:
|
|
||||||
assert len(vardef.size) == 2
|
|
||||||
self.p("{:s}\v.fill {:d}\t\t; matrix {:d}*{:d} bytes"
|
|
||||||
.format(vardef.name, vardef.size[0] * vardef.size[1], vardef.size[0], vardef.size[1]))
|
|
||||||
elif vardef.datatype in STRING_DATATYPES:
|
|
||||||
string_vars.append(vardef)
|
|
||||||
else:
|
|
||||||
raise CodeError("unknown variable type " + str(vardef.datatype))
|
|
||||||
if string_vars:
|
|
||||||
self.p("il65_string_vars_start")
|
|
||||||
for svar in sorted(string_vars, key=lambda v: v.name): # must be the same order as in the init routine!!!
|
|
||||||
self.p("{:s}\v.fill {:d}+1\t\t; {}".format(svar.name, len(svar.value), svar.datatype.name.lower()))
|
|
||||||
self.p("")
|
self.p("")
|
||||||
|
|
||||||
def _generate_string_var(self, vardef: VarDef, init: bool=False) -> None:
|
def _generate_string_var(self, vardef: VarDef, init: bool=False) -> None:
|
||||||
|
@ -381,13 +381,14 @@ class InlineAssembly(AstNode):
|
|||||||
assembly = attr.ib(type=str)
|
assembly = attr.ib(type=str)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False, slots=True)
|
||||||
class VarDef(AstNode):
|
class VarDef(AstNode):
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str)
|
||||||
vartype = attr.ib()
|
vartype = attr.ib()
|
||||||
datatype = attr.ib()
|
datatype = attr.ib()
|
||||||
value = attr.ib(default=None)
|
value = attr.ib(default=None)
|
||||||
size = attr.ib(type=list, default=None)
|
size = attr.ib(type=list, default=None)
|
||||||
|
zp_address = attr.ib(type=int, default=None, init=False) # the address in the zero page if this var is there, will be set later
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
# convert vartype to enum
|
# convert vartype to enum
|
||||||
@ -625,7 +626,9 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc
|
|||||||
if isinstance(value, VarDef):
|
if isinstance(value, VarDef):
|
||||||
if value.vartype == VarType.MEMORY:
|
if value.vartype == VarType.MEMORY:
|
||||||
return value.value
|
return value.value
|
||||||
raise ExpressionEvaluationError("taking the address of this {} isn't a constant".format(value.__class__.__name__), expr.name.sourceref)
|
if value.vartype == VarType.CONST:
|
||||||
|
raise ExpressionEvaluationError("can't take the address of a constant", expr.name.sourceref)
|
||||||
|
raise ExpressionEvaluationError("address-of this {} isn't a compile-time constant".format(value.__class__.__name__), expr.name.sourceref)
|
||||||
else:
|
else:
|
||||||
raise ExpressionEvaluationError("constant address required, not {}".format(value.__class__.__name__), expr.name.sourceref)
|
raise ExpressionEvaluationError("constant address required, not {}".format(value.__class__.__name__), expr.name.sourceref)
|
||||||
except LookupError as x:
|
except LookupError as x:
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from il65.handwritten.symbols import Zeropage, SymbolError, DataType # @todo
|
from il65.compile import Zeropage, CompileError
|
||||||
|
from il65.plyparse import ZpOptions
|
||||||
|
from il65.datatypes import DataType
|
||||||
def test_zp_configure_onlyonce():
|
|
||||||
zp = Zeropage()
|
|
||||||
zp.configure()
|
|
||||||
with pytest.raises(SymbolError):
|
|
||||||
zp.configure()
|
|
||||||
|
|
||||||
|
|
||||||
def test_zp_names():
|
def test_zp_names():
|
||||||
zp = Zeropage()
|
zp = Zeropage(ZpOptions.NOCLOBBER)
|
||||||
zp.configure()
|
|
||||||
zp.allocate("", DataType.BYTE)
|
zp.allocate("", DataType.BYTE)
|
||||||
zp.allocate("", DataType.BYTE)
|
zp.allocate("", DataType.BYTE)
|
||||||
zp.allocate("varname", DataType.BYTE)
|
zp.allocate("varname", DataType.BYTE)
|
||||||
@ -21,23 +15,22 @@ def test_zp_names():
|
|||||||
|
|
||||||
|
|
||||||
def test_zp_noclobber_allocation():
|
def test_zp_noclobber_allocation():
|
||||||
zp = Zeropage()
|
zp = Zeropage(ZpOptions.NOCLOBBER)
|
||||||
zp.configure(False)
|
|
||||||
assert zp.available() == 9
|
assert zp.available() == 9
|
||||||
with pytest.raises(LookupError):
|
with pytest.raises(CompileError):
|
||||||
zp.allocate("impossible", DataType.FLOAT) # in regular zp there aren't 5 sequential bytes free
|
zp.allocate("impossible", DataType.FLOAT) # in regular zp there aren't 5 sequential bytes free
|
||||||
for i in range(zp.available()):
|
for i in range(zp.available()):
|
||||||
zp.allocate("bytevar"+str(i), DataType.BYTE)
|
loc = zp.allocate("bytevar"+str(i), DataType.BYTE)
|
||||||
|
assert loc > 0
|
||||||
assert zp.available() == 0
|
assert zp.available() == 0
|
||||||
with pytest.raises(LookupError):
|
with pytest.raises(CompileError):
|
||||||
zp.allocate("", DataType.BYTE)
|
zp.allocate("", DataType.BYTE)
|
||||||
with pytest.raises(LookupError):
|
with pytest.raises(CompileError):
|
||||||
zp.allocate("", DataType.WORD)
|
zp.allocate("", DataType.WORD)
|
||||||
|
|
||||||
|
|
||||||
def test_zp_clobber_allocation():
|
def test_zp_clobber_allocation():
|
||||||
zp = Zeropage()
|
zp = Zeropage(ZpOptions.CLOBBER)
|
||||||
zp.configure(True)
|
|
||||||
assert zp.available() == 239
|
assert zp.available() == 239
|
||||||
loc = zp.allocate("", DataType.FLOAT)
|
loc = zp.allocate("", DataType.FLOAT)
|
||||||
assert loc > 3 and loc not in zp.free
|
assert loc > 3 and loc not in zp.free
|
||||||
@ -45,15 +38,28 @@ def test_zp_clobber_allocation():
|
|||||||
for _ in range(num-3):
|
for _ in range(num-3):
|
||||||
zp.allocate("", DataType.FLOAT)
|
zp.allocate("", DataType.FLOAT)
|
||||||
assert zp.available() == 19
|
assert zp.available() == 19
|
||||||
with pytest.raises(LookupError):
|
with pytest.raises(CompileError):
|
||||||
zp.allocate("", DataType.FLOAT) # can't allocate because no more sequential bytes, only fragmented
|
zp.allocate("", DataType.FLOAT) # can't allocate because no more sequential bytes, only fragmented
|
||||||
for _ in range(14):
|
for _ in range(14):
|
||||||
zp.allocate("", DataType.BYTE)
|
zp.allocate("", DataType.BYTE)
|
||||||
zp.allocate("", DataType.WORD)
|
zp.allocate("", DataType.WORD)
|
||||||
zp.allocate("", DataType.WORD)
|
zp.allocate("", DataType.WORD)
|
||||||
with pytest.raises(LookupError):
|
with pytest.raises(CompileError):
|
||||||
zp.allocate("", DataType.WORD)
|
zp.allocate("", DataType.WORD)
|
||||||
assert zp.available() == 1
|
assert zp.available() == 1
|
||||||
zp.allocate("last", DataType.BYTE)
|
zp.allocate("last", DataType.BYTE)
|
||||||
with pytest.raises(LookupError):
|
with pytest.raises(CompileError):
|
||||||
zp.allocate("impossible", DataType.BYTE)
|
zp.allocate("impossible", DataType.BYTE)
|
||||||
|
|
||||||
|
|
||||||
|
def test_zp_efficient_allocation():
|
||||||
|
# free = [0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa]
|
||||||
|
zp = Zeropage(ZpOptions.NOCLOBBER)
|
||||||
|
assert zp.available() == 9
|
||||||
|
assert 0x2a == zp.allocate("", DataType.BYTE)
|
||||||
|
assert 0x52 == zp.allocate("", DataType.BYTE)
|
||||||
|
assert 0x04 == zp.allocate("", DataType.WORD)
|
||||||
|
assert 0xf7 == zp.allocate("", DataType.WORD)
|
||||||
|
assert 0x06 == zp.allocate("", DataType.BYTE)
|
||||||
|
assert 0xf9 == zp.allocate("", DataType.WORD)
|
||||||
|
assert zp.available() == 0
|
||||||
|
@ -126,9 +126,7 @@
|
|||||||
; because zp-vars get assigned a specific address (from a pool). Also, it's a byte.
|
; because zp-vars get assigned a specific address (from a pool). Also, it's a byte.
|
||||||
|
|
||||||
var .word initword0a = &ZP.zpmem1
|
var .word initword0a = &ZP.zpmem1
|
||||||
var .word initword0 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
|
|
||||||
var initbytea0 = &ZP.zpmem1
|
var initbytea0 = &ZP.zpmem1
|
||||||
var .word initworda1 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
|
|
||||||
|
|
||||||
|
|
||||||
; (constant) expressions
|
; (constant) expressions
|
||||||
|
Loading…
Reference in New Issue
Block a user