fix clobberzp and zp config only once

This commit is contained in:
Irmen de Jong 2017-12-29 03:52:26 +01:00
parent 739f5b9659
commit 52d685b0fc
6 changed files with 61 additions and 16 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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
View 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
}

View File

@ -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``.