mirror of
https://github.com/irmen/prog8.git
synced 2024-12-01 15:52:54 +00:00
fix clobberzp and zp config only once
This commit is contained in:
parent
739f5b9659
commit
52d685b0fc
@ -17,7 +17,7 @@ from typing import Set, List, Tuple, Optional, Any, Dict, Union, Generator
|
|||||||
from .astparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\
|
from .astparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\
|
||||||
parse_expr_as_string, parse_arguments, parse_expr_as_comparison
|
parse_expr_as_string, parse_arguments, parse_expr_as_comparison
|
||||||
from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \
|
from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \
|
||||||
zeropage, check_value_in_range, char_to_bytevalue, \
|
Zeropage, check_value_in_range, char_to_bytevalue, \
|
||||||
PrimitiveType, VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \
|
PrimitiveType, VariableDef, ConstantDef, SymbolError, STRING_DATATYPES, \
|
||||||
REGISTER_SYMBOLS, REGISTER_WORDS, REGISTER_BYTES, REGISTER_SBITS, RESERVED_NAMES
|
REGISTER_SYMBOLS, REGISTER_WORDS, REGISTER_BYTES, REGISTER_SBITS, RESERVED_NAMES
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ class ParseResult:
|
|||||||
self.start_address = 0
|
self.start_address = 0
|
||||||
self.blocks = [] # type: List['ParseResult.Block']
|
self.blocks = [] # type: List['ParseResult.Block']
|
||||||
self.subroutine_usage = defaultdict(set) # type: Dict[Tuple[str, str], Set[str]]
|
self.subroutine_usage = defaultdict(set) # type: Dict[Tuple[str, str], Set[str]]
|
||||||
|
self.zeropage = Zeropage()
|
||||||
|
|
||||||
def all_blocks(self) -> Generator['ParseResult.Block', None, None]:
|
def all_blocks(self) -> Generator['ParseResult.Block', None, None]:
|
||||||
for block in self.blocks:
|
for block in self.blocks:
|
||||||
@ -463,6 +464,11 @@ class ParseResult:
|
|||||||
for name, value in self.arguments or []:
|
for name, value in self.arguments or []:
|
||||||
assert name is not None, "all call arguments should have a name or be matched on a named parameter"
|
assert name is not None, "all call arguments should have a name or be matched on a named parameter"
|
||||||
assignment = parser.parse_assignment(name, value)
|
assignment = parser.parse_assignment(name, value)
|
||||||
|
if assignment.leftvalues[0].datatype != DataType.BYTE:
|
||||||
|
if isinstance(assignment.right, ParseResult.IntegerValue) and assignment.right.constant:
|
||||||
|
# a call that doesn't expect a BYTE argument but gets one, converted from a 1-byte string most likely
|
||||||
|
if value.startswith("'") and value.endswith("'"):
|
||||||
|
parser.print_warning("possible problematic string to byte conversion (use a .text var instead?)")
|
||||||
if not assignment.is_identity():
|
if not assignment.is_identity():
|
||||||
assignment.lineno = self.lineno
|
assignment.lineno = self.lineno
|
||||||
self.desugared_call_arguments.append(assignment)
|
self.desugared_call_arguments.append(assignment)
|
||||||
@ -562,6 +568,7 @@ class Parser:
|
|||||||
self._cur_lineidx = -1 # used to efficiently go to next/previous line in source
|
self._cur_lineidx = -1 # used to efficiently go to next/previous line in source
|
||||||
self.cur_block = None # type: ParseResult.Block
|
self.cur_block = None # type: ParseResult.Block
|
||||||
self.root_scope = SymbolTable("<root>", None, None)
|
self.root_scope = SymbolTable("<root>", None, None)
|
||||||
|
self.root_scope.set_zeropage(self.result.zeropage)
|
||||||
self.ppsymbols = ppsymbols # symboltable from preprocess phase
|
self.ppsymbols = ppsymbols # symboltable from preprocess phase
|
||||||
self.print_block_parsing = True
|
self.print_block_parsing = True
|
||||||
|
|
||||||
@ -642,7 +649,8 @@ class Parser:
|
|||||||
self.cur_block = ParseResult.Block("<header>", self.sourceref, self.root_scope)
|
self.cur_block = ParseResult.Block("<header>", self.sourceref, self.root_scope)
|
||||||
self.result.add_block(self.cur_block)
|
self.result.add_block(self.cur_block)
|
||||||
self.parse_header()
|
self.parse_header()
|
||||||
zeropage.configure(self.result.clobberzp)
|
if not self.parsing_import:
|
||||||
|
self.result.zeropage.configure(self.result.clobberzp)
|
||||||
while True:
|
while True:
|
||||||
self._parse_comments()
|
self._parse_comments()
|
||||||
next_line = self.peek_next_line().lstrip()
|
next_line = self.peek_next_line().lstrip()
|
||||||
@ -1030,7 +1038,7 @@ class Parser:
|
|||||||
varname, datatype, length, dimensions, valuetext = self.parse_def_common(line, "memory")
|
varname, datatype, length, dimensions, valuetext = self.parse_def_common(line, "memory")
|
||||||
memaddress = parse_expr_as_int(valuetext, self.cur_block.symbols, self.ppsymbols, self.sourceref)
|
memaddress = parse_expr_as_int(valuetext, self.cur_block.symbols, self.ppsymbols, self.sourceref)
|
||||||
if is_zeropage and memaddress > 0xff:
|
if is_zeropage and memaddress > 0xff:
|
||||||
raise self.PError("address must lie in zeropage $00-$ff")
|
raise self.PError("address must be in zeropage $00-$ff")
|
||||||
try:
|
try:
|
||||||
self.cur_block.symbols.define_variable(varname, self.sourceref, datatype,
|
self.cur_block.symbols.define_variable(varname, self.sourceref, datatype,
|
||||||
length=length, address=memaddress, matrixsize=dimensions)
|
length=length, address=memaddress, matrixsize=dimensions)
|
||||||
@ -1267,6 +1275,15 @@ class Parser:
|
|||||||
if any(not a[0] for a in arguments or []):
|
if any(not a[0] for a in arguments or []):
|
||||||
raise self.PError("all call arguments should have a name or be matched on a named parameter")
|
raise self.PError("all call arguments should have a name or be matched on a named parameter")
|
||||||
if isinstance(target, (type(None), ParseResult.Value)):
|
if isinstance(target, (type(None), ParseResult.Value)):
|
||||||
|
# special case for the C-64 lib's print function, to be able to use it with a single character argument
|
||||||
|
if target.name == "c64util.print_string" and len(arguments) == 1 and len(arguments[0][0]) > 1:
|
||||||
|
if arguments[0][1].startswith("'") and arguments[0][1].endswith("'"):
|
||||||
|
target = self.parse_expression("c64.CHROUT")
|
||||||
|
address = target.address
|
||||||
|
outputvars = None
|
||||||
|
_, newsymbol = self.lookup_with_ppsymbols("c64.CHROUT")
|
||||||
|
assert len(newsymbol.parameters) == 1
|
||||||
|
arguments = [(newsymbol.parameters[0][1], arguments[0][1])]
|
||||||
if is_goto:
|
if is_goto:
|
||||||
return ParseResult.CallStmt(self.sourceref.line, target, address=address,
|
return ParseResult.CallStmt(self.sourceref.line, target, address=address,
|
||||||
arguments=arguments, outputs=outputvars, is_goto=True, condition=condition)
|
arguments=arguments, outputs=outputvars, is_goto=True, condition=condition)
|
||||||
|
@ -12,8 +12,8 @@ from .symbols import SourceRef
|
|||||||
|
|
||||||
|
|
||||||
class PreprocessingParser(Parser):
|
class PreprocessingParser(Parser):
|
||||||
def __init__(self, filename: str) -> None:
|
def __init__(self, filename: str, parsing_import: bool=False) -> None:
|
||||||
super().__init__(filename, "", parsing_import=True)
|
super().__init__(filename, "", parsing_import=parsing_import)
|
||||||
self.print_block_parsing = False
|
self.print_block_parsing = False
|
||||||
|
|
||||||
def preprocess(self) -> Tuple[List[Tuple[int, str]], SymbolTable]:
|
def preprocess(self) -> Tuple[List[Tuple[int, str]], SymbolTable]:
|
||||||
@ -63,4 +63,4 @@ class PreprocessingParser(Parser):
|
|||||||
super().parse_subroutine_def(line)
|
super().parse_subroutine_def(line)
|
||||||
|
|
||||||
def create_import_parser(self, filename: str, outputdir: str) -> 'Parser':
|
def create_import_parser(self, filename: str, outputdir: str) -> 'Parser':
|
||||||
return PreprocessingParser(filename)
|
return PreprocessingParser(filename, parsing_import=True)
|
||||||
|
@ -212,8 +212,11 @@ class Zeropage:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.unused_bytes = [] # type: List[int]
|
self.unused_bytes = [] # type: List[int]
|
||||||
self.unused_words = [] # type: List[int]
|
self.unused_words = [] # type: List[int]
|
||||||
|
self._configured = False
|
||||||
|
|
||||||
def configure(self, clobber_zp: bool = False) -> None:
|
def configure(self, clobber_zp: bool = False) -> None:
|
||||||
|
if self._configured:
|
||||||
|
raise SymbolError("cannot configure the ZP multiple times")
|
||||||
if clobber_zp:
|
if clobber_zp:
|
||||||
self.unused_bytes = list(range(0x04, 0x80))
|
self.unused_bytes = list(range(0x04, 0x80))
|
||||||
self.unused_words = list(range(0x80, 0x100, 2))
|
self.unused_words = list(range(0x80, 0x100, 2))
|
||||||
@ -224,6 +227,7 @@ class Zeropage:
|
|||||||
self.unused_words = [0x04, 0xf7, 0xf9, 0xfb, 0xfd] # 5 zp variables (16 bits each)
|
self.unused_words = [0x04, 0xf7, 0xf9, 0xfb, 0xfd] # 5 zp variables (16 bits each)
|
||||||
assert self.SCRATCH_B1 not in self.unused_bytes and self.SCRATCH_B1 not in self.unused_words
|
assert self.SCRATCH_B1 not in self.unused_bytes and self.SCRATCH_B1 not in self.unused_words
|
||||||
assert self.SCRATCH_B2 not in self.unused_bytes and self.SCRATCH_B2 not in self.unused_words
|
assert self.SCRATCH_B2 not in self.unused_bytes and self.SCRATCH_B2 not in self.unused_words
|
||||||
|
self._configured = True
|
||||||
|
|
||||||
def get_unused_byte(self):
|
def get_unused_byte(self):
|
||||||
return self.unused_bytes.pop()
|
return self.unused_bytes.pop()
|
||||||
@ -240,10 +244,6 @@ class Zeropage:
|
|||||||
return len(self.unused_words)
|
return len(self.unused_words)
|
||||||
|
|
||||||
|
|
||||||
# the single, global Zeropage object
|
|
||||||
zeropage = Zeropage()
|
|
||||||
|
|
||||||
|
|
||||||
class SymbolTable:
|
class SymbolTable:
|
||||||
|
|
||||||
def __init__(self, name: str, parent: Optional['SymbolTable'], owning_block: Any) -> None:
|
def __init__(self, name: str, parent: Optional['SymbolTable'], owning_block: Any) -> None:
|
||||||
@ -252,6 +252,13 @@ class SymbolTable:
|
|||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.owning_block = owning_block
|
self.owning_block = owning_block
|
||||||
self.eval_dict = None
|
self.eval_dict = None
|
||||||
|
self._zeropage = parent._zeropage if parent else None
|
||||||
|
|
||||||
|
def set_zeropage(self, zp: Zeropage) -> None:
|
||||||
|
if self._zeropage is None:
|
||||||
|
self._zeropage = zp
|
||||||
|
else:
|
||||||
|
raise SymbolError("already have a zp")
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield from self.symbols.values()
|
yield from self.symbols.values()
|
||||||
@ -358,17 +365,17 @@ class SymbolTable:
|
|||||||
if datatype == DataType.BYTE:
|
if datatype == DataType.BYTE:
|
||||||
if allocate and self.name == "ZP":
|
if allocate and self.name == "ZP":
|
||||||
try:
|
try:
|
||||||
address = zeropage.get_unused_byte()
|
address = self._zeropage.get_unused_byte()
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise SymbolError("too many global 8-bit variables in ZP")
|
raise SymbolError("no space in ZP left for more global 8-bit variables (try zp clobber)")
|
||||||
self.symbols[name] = VariableDef(self.name, name, sourceref, DataType.BYTE, allocate,
|
self.symbols[name] = VariableDef(self.name, name, sourceref, DataType.BYTE, allocate,
|
||||||
value=value, length=1, address=address)
|
value=value, length=1, address=address)
|
||||||
elif datatype == DataType.WORD:
|
elif datatype == DataType.WORD:
|
||||||
if allocate and self.name == "ZP":
|
if allocate and self.name == "ZP":
|
||||||
try:
|
try:
|
||||||
address = zeropage.get_unused_word()
|
address = self._zeropage.get_unused_word()
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise SymbolError("too many global 16-bit variables in ZP")
|
raise SymbolError("no space in ZP left for more global 16-bit variables (try zp clobber)")
|
||||||
self.symbols[name] = VariableDef(self.name, name, sourceref, DataType.WORD, allocate,
|
self.symbols[name] = VariableDef(self.name, name, sourceref, DataType.WORD, allocate,
|
||||||
value=value, length=1, address=address)
|
value=value, length=1, address=address)
|
||||||
elif datatype == DataType.FLOAT:
|
elif datatype == DataType.FLOAT:
|
||||||
|
@ -333,8 +333,6 @@ sub GETADRAY () -> (AY, X?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
; @todo optimize print_string()/ print_pstring() of a single character into a call to c64.CHROUT instead
|
|
||||||
|
|
||||||
sub print_string (address: XY) -> (A?, Y?) {
|
sub print_string (address: XY) -> (A?, Y?) {
|
||||||
; ---- print null terminated string from X/Y
|
; ---- print null terminated string from X/Y
|
||||||
asm {
|
asm {
|
||||||
|
18
lib/mathlib.ill
Normal file
18
lib/mathlib.ill
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
; IL65 integer math library for 6502
|
||||||
|
; (floating point math is done via the C-64's BASIC ROM routines)
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net)
|
||||||
|
; License: GNU GPL 3.0, see LICENSE
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
|
||||||
|
output raw
|
||||||
|
|
||||||
|
~ math {
|
||||||
|
|
||||||
|
|
||||||
|
; @todo some interesting routines can be found here http://6502org.wikidot.com/software-math
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -126,6 +126,11 @@ IL65 supports the following data types:
|
|||||||
Strings can be writen in your code as CBM PETSCII or as C-64 screencode variants,
|
Strings can be writen in your code as CBM PETSCII or as C-64 screencode variants,
|
||||||
these will be translated by the compiler. PETSCII is the default, if you need screencodes you
|
these will be translated by the compiler. PETSCII is the default, if you need screencodes you
|
||||||
have to use the ``s`` variants of the type identifier.
|
have to use the ``s`` variants of the type identifier.
|
||||||
|
If you write a string with just one character in it, it is *always* considered to be a BYTE instead with
|
||||||
|
that character's PETSCII value. So if you really need a string of length 1 you must declare it
|
||||||
|
explicitly as a variable of type ``.text``, you cannot put ``"x"`` as a subroutine argument where
|
||||||
|
the subroutine expects (the address of) a string. IL65's type system is unfortunately not strict enough to
|
||||||
|
avoid this mistake, but it does print a warning if the situation is detected.
|
||||||
|
|
||||||
For many floating point operations, the compiler has to use routines in the C-64 BASIC and KERNAL ROMs.
|
For many floating point operations, the compiler has to use routines in the C-64 BASIC and KERNAL ROMs.
|
||||||
So they will only work if the BASIC ROM (and KERNAL ROM) are banked in, and your code imports the ``c654lib.ill``.
|
So they will only work if the BASIC ROM (and KERNAL ROM) are banked in, and your code imports the ``c654lib.ill``.
|
||||||
|
Loading…
Reference in New Issue
Block a user