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,\
parse_expr_as_string, parse_arguments, parse_expr_as_comparison
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, \
REGISTER_SYMBOLS, REGISTER_WORDS, REGISTER_BYTES, REGISTER_SBITS, RESERVED_NAMES
@ -37,6 +37,7 @@ class ParseResult:
self.start_address = 0
self.blocks = [] # type: List['ParseResult.Block']
self.subroutine_usage = defaultdict(set) # type: Dict[Tuple[str, str], Set[str]]
self.zeropage = Zeropage()
def all_blocks(self) -> Generator['ParseResult.Block', None, None]:
for block in self.blocks:
@ -463,6 +464,11 @@ class ParseResult:
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"
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():
assignment.lineno = self.lineno
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_block = None # type: ParseResult.Block
self.root_scope = SymbolTable("<root>", None, None)
self.root_scope.set_zeropage(self.result.zeropage)
self.ppsymbols = ppsymbols # symboltable from preprocess phase
self.print_block_parsing = True
@ -642,7 +649,8 @@ class Parser:
self.cur_block = ParseResult.Block("<header>", self.sourceref, self.root_scope)
self.result.add_block(self.cur_block)
self.parse_header()
zeropage.configure(self.result.clobberzp)
if not self.parsing_import:
self.result.zeropage.configure(self.result.clobberzp)
while True:
self._parse_comments()
next_line = self.peek_next_line().lstrip()
@ -1030,7 +1038,7 @@ class Parser:
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)
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:
self.cur_block.symbols.define_variable(varname, self.sourceref, datatype,
length=length, address=memaddress, matrixsize=dimensions)
@ -1267,6 +1275,15 @@ class Parser:
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")
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:
return ParseResult.CallStmt(self.sourceref.line, target, address=address,
arguments=arguments, outputs=outputvars, is_goto=True, condition=condition)

View File

@ -12,8 +12,8 @@ from .symbols import SourceRef
class PreprocessingParser(Parser):
def __init__(self, filename: str) -> None:
super().__init__(filename, "", parsing_import=True)
def __init__(self, filename: str, parsing_import: bool=False) -> None:
super().__init__(filename, "", parsing_import=parsing_import)
self.print_block_parsing = False
def preprocess(self) -> Tuple[List[Tuple[int, str]], SymbolTable]:
@ -63,4 +63,4 @@ class PreprocessingParser(Parser):
super().parse_subroutine_def(line)
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:
self.unused_bytes = [] # type: List[int]
self.unused_words = [] # type: List[int]
self._configured = False
def configure(self, clobber_zp: bool = False) -> None:
if self._configured:
raise SymbolError("cannot configure the ZP multiple times")
if clobber_zp:
self.unused_bytes = list(range(0x04, 0x80))
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)
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
self._configured = True
def get_unused_byte(self):
return self.unused_bytes.pop()
@ -240,10 +244,6 @@ class Zeropage:
return len(self.unused_words)
# the single, global Zeropage object
zeropage = Zeropage()
class SymbolTable:
def __init__(self, name: str, parent: Optional['SymbolTable'], owning_block: Any) -> None:
@ -252,6 +252,13 @@ class SymbolTable:
self.parent = parent
self.owning_block = owning_block
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):
yield from self.symbols.values()
@ -358,17 +365,17 @@ class SymbolTable:
if datatype == DataType.BYTE:
if allocate and self.name == "ZP":
try:
address = zeropage.get_unused_byte()
address = self._zeropage.get_unused_byte()
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,
value=value, length=1, address=address)
elif datatype == DataType.WORD:
if allocate and self.name == "ZP":
try:
address = zeropage.get_unused_word()
address = self._zeropage.get_unused_word()
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,
value=value, length=1, address=address)
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?) {
; ---- print null terminated string from X/Y
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,
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.
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.
So they will only work if the BASIC ROM (and KERNAL ROM) are banked in, and your code imports the ``c654lib.ill``.