mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +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,\
|
||||
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)
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
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,
|
||||
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``.
|
||||
|
Loading…
Reference in New Issue
Block a user