mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
changes to call
This commit is contained in:
parent
fbb4ba4bd8
commit
37f049ee54
@ -110,13 +110,6 @@ def parse_expr_as_primitive(text: str, context: Optional[SymbolTable], ppcontext
|
|||||||
raise src.to_error("int or float or string expected, not " + type(result).__name__)
|
raise src.to_error("int or float or string expected, not " + type(result).__name__)
|
||||||
|
|
||||||
|
|
||||||
def parse_statement(text: str, sourceref: SourceRef) -> int: # @todo in progress...
|
|
||||||
src = SourceLine(text, sourceref)
|
|
||||||
text = src.preprocess()
|
|
||||||
node = ast.parse(text, sourceref.file, mode="single")
|
|
||||||
return node
|
|
||||||
|
|
||||||
|
|
||||||
class EvaluatingTransformer(ast.NodeTransformer):
|
class EvaluatingTransformer(ast.NodeTransformer):
|
||||||
def __init__(self, src: SourceLine, context: SymbolTable, ppcontext: SymbolTable) -> None:
|
def __init__(self, src: SourceLine, context: SymbolTable, ppcontext: SymbolTable) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -14,7 +14,7 @@ import contextlib
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import TextIO, Set, Union
|
from typing import TextIO, Set, Union
|
||||||
from .parse import ProgramFormat, ParseResult, Parser
|
from .parse import ProgramFormat, ParseResult, Parser
|
||||||
from .symbols import Zeropage, DataType, VariableDef, SubroutineDef, \
|
from .symbols import Zeropage, DataType, ConstantDef, VariableDef, SubroutineDef, \
|
||||||
STRING_DATATYPES, REGISTER_WORDS, FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE
|
STRING_DATATYPES, REGISTER_WORDS, FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE
|
||||||
|
|
||||||
|
|
||||||
@ -246,10 +246,11 @@ class CodeGenerator:
|
|||||||
for constdef in consts:
|
for constdef in consts:
|
||||||
if constdef.type == DataType.FLOAT:
|
if constdef.type == DataType.FLOAT:
|
||||||
self.p("\t\t{:s} = {}".format(constdef.name, constdef.value))
|
self.p("\t\t{:s} = {}".format(constdef.name, constdef.value))
|
||||||
elif constdef.type in STRING_DATATYPES:
|
|
||||||
print("warning: {}: const string not defined in output yet".format(constdef.sourceref)) # XXX
|
|
||||||
elif constdef.type in (DataType.BYTE, DataType.WORD):
|
elif constdef.type in (DataType.BYTE, DataType.WORD):
|
||||||
self.p("\t\t{:s} = {:s}".format(constdef.name, Parser.to_hex(constdef.value))) # type: ignore
|
self.p("\t\t{:s} = {:s}".format(constdef.name, Parser.to_hex(constdef.value))) # type: ignore
|
||||||
|
elif constdef.type in STRING_DATATYPES:
|
||||||
|
# a const string is just a string variable in the generated assembly
|
||||||
|
self._generate_string_var(constdef)
|
||||||
else:
|
else:
|
||||||
raise CodeError("invalid const type", constdef)
|
raise CodeError("invalid const type", constdef)
|
||||||
mem_vars = [vi for vi in block.symbols.iter_variables() if not vi.allocate and not vi.register]
|
mem_vars = [vi for vi in block.symbols.iter_variables() if not vi.allocate and not vi.register]
|
||||||
@ -306,25 +307,29 @@ class CodeGenerator:
|
|||||||
vardef.matrixsize[0] * vardef.matrixsize[1],
|
vardef.matrixsize[0] * vardef.matrixsize[1],
|
||||||
vardef.value or 0,
|
vardef.value or 0,
|
||||||
vardef.matrixsize[0], vardef.matrixsize[1]))
|
vardef.matrixsize[0], vardef.matrixsize[1]))
|
||||||
elif vardef.type == DataType.STRING:
|
elif vardef.type in STRING_DATATYPES:
|
||||||
# 0-terminated string
|
self._generate_string_var(vardef)
|
||||||
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
|
||||||
elif vardef.type == DataType.STRING_P:
|
|
||||||
# pascal string
|
|
||||||
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
|
||||||
elif vardef.type == DataType.STRING_S:
|
|
||||||
# 0-terminated string in screencode encoding
|
|
||||||
self.p(".enc 'screen'")
|
|
||||||
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
|
||||||
self.p(".enc 'none'")
|
|
||||||
elif vardef.type == DataType.STRING_PS:
|
|
||||||
# 0-terminated pascal string in screencode encoding
|
|
||||||
self.p(".enc 'screen'")
|
|
||||||
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
|
||||||
self.p(".enc 'none'")
|
|
||||||
else:
|
else:
|
||||||
raise CodeError("unknown variable type " + str(vardef.type))
|
raise CodeError("unknown variable type " + str(vardef.type))
|
||||||
|
|
||||||
|
def _generate_string_var(self, vardef: Union[ConstantDef, VariableDef]) -> None:
|
||||||
|
if vardef.type == DataType.STRING:
|
||||||
|
# 0-terminated string
|
||||||
|
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
||||||
|
elif vardef.type == DataType.STRING_P:
|
||||||
|
# pascal string
|
||||||
|
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
||||||
|
elif vardef.type == DataType.STRING_S:
|
||||||
|
# 0-terminated string in screencode encoding
|
||||||
|
self.p(".enc 'screen'")
|
||||||
|
self.p("{:s}\n\t\t.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
||||||
|
self.p(".enc 'none'")
|
||||||
|
elif vardef.type == DataType.STRING_PS:
|
||||||
|
# 0-terminated pascal string in screencode encoding
|
||||||
|
self.p(".enc 'screen'")
|
||||||
|
self.p("{:s}\n\t\t.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
||||||
|
self.p(".enc 'none'")
|
||||||
|
|
||||||
def generate_statement(self, stmt: ParseResult._AstNode) -> None:
|
def generate_statement(self, stmt: ParseResult._AstNode) -> None:
|
||||||
if isinstance(stmt, ParseResult.ReturnStmt):
|
if isinstance(stmt, ParseResult.ReturnStmt):
|
||||||
if stmt.a:
|
if stmt.a:
|
||||||
@ -566,30 +571,31 @@ class CodeGenerator:
|
|||||||
else:
|
else:
|
||||||
raise CodeError("invalid assignment target (4)", str(stmt))
|
raise CodeError("invalid assignment target (4)", str(stmt))
|
||||||
elif isinstance(rvalue, ParseResult.FloatValue):
|
elif isinstance(rvalue, ParseResult.FloatValue):
|
||||||
mflpt = self.to_mflpt5(rvalue.value)
|
|
||||||
for lv in stmt.leftvalues:
|
for lv in stmt.leftvalues:
|
||||||
if isinstance(lv, ParseResult.MemMappedValue) and lv.datatype == DataType.FLOAT:
|
if isinstance(lv, ParseResult.MemMappedValue) and lv.datatype == DataType.FLOAT:
|
||||||
self.generate_store_immediate_float(lv, rvalue.value, mflpt)
|
self.generate_assign_float_to_mem(lv, rvalue)
|
||||||
elif isinstance(lv, ParseResult.IndirectValue):
|
elif isinstance(lv, ParseResult.IndirectValue):
|
||||||
lv = unwrap_indirect(lv)
|
lv = unwrap_indirect(lv)
|
||||||
assert lv.datatype == DataType.FLOAT
|
assert lv.datatype == DataType.FLOAT
|
||||||
self.generate_store_immediate_float(lv, rvalue.value, mflpt)
|
self.generate_assign_float_to_mem(lv, rvalue)
|
||||||
else:
|
else:
|
||||||
raise CodeError("cannot assign float to ", str(lv))
|
raise CodeError("cannot assign float to ", str(lv))
|
||||||
else:
|
else:
|
||||||
raise CodeError("invalid assignment value type", str(stmt))
|
raise CodeError("invalid assignment value type", str(stmt))
|
||||||
|
|
||||||
def generate_store_immediate_float(self, mmv: ParseResult.MemMappedValue, floatvalue: float,
|
def generate_assign_float_to_mem(self, mmv: ParseResult.MemMappedValue,
|
||||||
mflpt: bytearray, emit_pha: bool=True) -> None:
|
rvalue: Union[ParseResult.FloatValue, ParseResult.IntegerValue], save_reg: bool=True) -> None:
|
||||||
|
floatvalue = float(rvalue.value)
|
||||||
|
mflpt = self.to_mflpt5(floatvalue)
|
||||||
target = mmv.name or Parser.to_hex(mmv.address)
|
target = mmv.name or Parser.to_hex(mmv.address)
|
||||||
if emit_pha:
|
if save_reg:
|
||||||
self.p("\t\tpha\t\t\t; {:s} = {}".format(target, floatvalue))
|
self.p("\t\tpha\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue))
|
||||||
else:
|
else:
|
||||||
self.p("\t\t\t\t\t; {:s} = {}".format(target, floatvalue))
|
self.p("\t\t\t\t\t; {:s} = {}".format(target, rvalue.name or floatvalue))
|
||||||
for num in range(5):
|
for num in range(5):
|
||||||
self.p("\t\tlda #${:02x}".format(mflpt[num]))
|
self.p("\t\tlda #${:02x}".format(mflpt[num]))
|
||||||
self.p("\t\tsta {:s}+{:d}".format(target, num))
|
self.p("\t\tsta {:s}+{:d}".format(target, num))
|
||||||
if emit_pha:
|
if save_reg:
|
||||||
self.p("\t\tpla")
|
self.p("\t\tpla")
|
||||||
|
|
||||||
def generate_assign_reg_to_memory(self, lv: ParseResult.MemMappedValue, r_register: str) -> None:
|
def generate_assign_reg_to_memory(self, lv: ParseResult.MemMappedValue, r_register: str) -> None:
|
||||||
@ -732,8 +738,7 @@ class CodeGenerator:
|
|||||||
elif lvdatatype == DataType.FLOAT:
|
elif lvdatatype == DataType.FLOAT:
|
||||||
if rvalue.value is not None and not DataType.FLOAT.assignable_from_value(rvalue.value):
|
if rvalue.value is not None and not DataType.FLOAT.assignable_from_value(rvalue.value):
|
||||||
raise ValueError("value cannot be assigned to a float")
|
raise ValueError("value cannot be assigned to a float")
|
||||||
floatvalue = float(rvalue.value)
|
self.generate_assign_float_to_mem(lv, rvalue, False)
|
||||||
self.generate_store_immediate_float(lv, floatvalue, self.to_mflpt5(floatvalue), False)
|
|
||||||
else:
|
else:
|
||||||
raise TypeError("invalid lvalue type " + str(lvdatatype))
|
raise TypeError("invalid lvalue type " + str(lvdatatype))
|
||||||
|
|
||||||
@ -744,15 +749,20 @@ class CodeGenerator:
|
|||||||
raise CodeError("can only assign a byte to a register")
|
raise CodeError("can only assign a byte to a register")
|
||||||
self.p("\t\tld{:s} {:s}".format(l_register.lower(), r_str))
|
self.p("\t\tld{:s} {:s}".format(l_register.lower(), r_str))
|
||||||
else:
|
else:
|
||||||
if rvalue.datatype != DataType.WORD:
|
if rvalue.datatype == DataType.BYTE:
|
||||||
raise CodeError("can only assign a word to a register pair")
|
self.p("\t\tld{:s} {:s}".format(l_register[0].lower(), r_str))
|
||||||
raise NotImplementedError("some mmap type assignment") # @todo other mmapped types
|
self.p("\t\tld{:s} #0".format(l_register[1].lower()))
|
||||||
|
elif rvalue.datatype == DataType.WORD:
|
||||||
|
self.p("\t\tld{:s} {:s}".format(l_register[0].lower(), r_str))
|
||||||
|
self.p("\t\tld{:s} {:s}+1".format(l_register[1].lower(), r_str))
|
||||||
|
else:
|
||||||
|
raise CodeError("can only assign a byte or word to a register pair")
|
||||||
|
|
||||||
def generate_assign_mem_to_mem(self, lv: ParseResult.MemMappedValue, rvalue: ParseResult.MemMappedValue) -> None:
|
def generate_assign_mem_to_mem(self, lv: ParseResult.MemMappedValue, rvalue: ParseResult.MemMappedValue) -> None:
|
||||||
r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.address)
|
r_str = rvalue.name if rvalue.name else "${:x}".format(rvalue.address)
|
||||||
if lv.datatype == DataType.BYTE:
|
if lv.datatype == DataType.BYTE:
|
||||||
if rvalue.datatype != DataType.BYTE:
|
if rvalue.datatype != DataType.BYTE:
|
||||||
raise CodeError("can only assign a byte to a byte")
|
raise CodeError("can only assign a byte to a byte", str(rvalue))
|
||||||
with self.preserving_registers({'A'}):
|
with self.preserving_registers({'A'}):
|
||||||
self.p("\t\tlda " + r_str)
|
self.p("\t\tlda " + r_str)
|
||||||
self.p("\t\tsta " + (lv.name or Parser.to_hex(lv.address)))
|
self.p("\t\tsta " + (lv.name or Parser.to_hex(lv.address)))
|
||||||
@ -760,9 +770,9 @@ class CodeGenerator:
|
|||||||
if rvalue.datatype == DataType.BYTE:
|
if rvalue.datatype == DataType.BYTE:
|
||||||
with self.preserving_registers({'A'}):
|
with self.preserving_registers({'A'}):
|
||||||
l_str = lv.name or Parser.to_hex(lv.address)
|
l_str = lv.name or Parser.to_hex(lv.address)
|
||||||
self.p("\t\tlda #0")
|
|
||||||
self.p("\t\tsta " + l_str)
|
|
||||||
self.p("\t\tlda " + r_str)
|
self.p("\t\tlda " + r_str)
|
||||||
|
self.p("\t\tsta " + l_str)
|
||||||
|
self.p("\t\tlda #0")
|
||||||
self.p("\t\tsta {:s}+1".format(l_str))
|
self.p("\t\tsta {:s}+1".format(l_str))
|
||||||
elif rvalue.datatype == DataType.WORD:
|
elif rvalue.datatype == DataType.WORD:
|
||||||
with self.preserving_registers({'A'}):
|
with self.preserving_registers({'A'}):
|
||||||
@ -772,10 +782,10 @@ class CodeGenerator:
|
|||||||
self.p("\t\tlda {:s}+1".format(r_str))
|
self.p("\t\tlda {:s}+1".format(r_str))
|
||||||
self.p("\t\tsta {:s}+1".format(l_str))
|
self.p("\t\tsta {:s}+1".format(l_str))
|
||||||
else:
|
else:
|
||||||
raise CodeError("can only assign a byte or word to a word")
|
raise CodeError("can only assign a byte or word to a word", str(rvalue))
|
||||||
else:
|
else:
|
||||||
raise CodeError("can only assign to a memory mapped byte or word value for now "
|
raise CodeError("can only assign memory to a memory mapped byte or word value for now "
|
||||||
"(if you need other types, can't you use a var?)")
|
"(if you need other types, can't you use a var?)", str(rvalue))
|
||||||
|
|
||||||
def generate_assign_char_to_memory(self, lv: ParseResult.MemMappedValue, char_str: str) -> None:
|
def generate_assign_char_to_memory(self, lv: ParseResult.MemMappedValue, char_str: str) -> None:
|
||||||
# Memory = Character
|
# Memory = Character
|
||||||
|
142
il65/parse.py
142
il65/parse.py
@ -92,8 +92,7 @@ class ParseResult:
|
|||||||
|
|
||||||
class IndirectValue(Value):
|
class IndirectValue(Value):
|
||||||
def __init__(self, value: 'ParseResult.Value', type_modifier: DataType) -> None:
|
def __init__(self, value: 'ParseResult.Value', type_modifier: DataType) -> None:
|
||||||
if not type_modifier:
|
assert type_modifier
|
||||||
type_modifier = value.datatype
|
|
||||||
super().__init__(type_modifier, value.name, False)
|
super().__init__(type_modifier, value.name, False)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
@ -103,7 +102,19 @@ class ParseResult:
|
|||||||
def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]:
|
def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]:
|
||||||
if self.constant:
|
if self.constant:
|
||||||
return False, "cannot assign to a constant"
|
return False, "cannot assign to a constant"
|
||||||
if other.datatype in {DataType.BYTE, DataType.WORD, DataType.FLOAT} | STRING_DATATYPES:
|
if self.datatype == DataType.BYTE:
|
||||||
|
if other.datatype == DataType.BYTE:
|
||||||
|
return True, ""
|
||||||
|
if self.datatype == DataType.WORD:
|
||||||
|
if other.datatype in {DataType.BYTE, DataType.WORD} | STRING_DATATYPES:
|
||||||
|
return True, ""
|
||||||
|
if self.datatype == DataType.FLOAT:
|
||||||
|
if other.datatype in {DataType.BYTE, DataType.WORD, DataType.FLOAT}:
|
||||||
|
return True, ""
|
||||||
|
if isinstance(other, (ParseResult.IntegerValue, ParseResult.FloatValue, ParseResult.StringValue)):
|
||||||
|
rangefault = check_value_in_range(self.datatype, "", 1, other.value)
|
||||||
|
if rangefault:
|
||||||
|
return False, rangefault
|
||||||
return True, ""
|
return True, ""
|
||||||
return False, "incompatible value for indirect assignment (need byte, word, float or string)"
|
return False, "incompatible value for indirect assignment (need byte, word, float or string)"
|
||||||
|
|
||||||
@ -209,10 +220,15 @@ class ParseResult:
|
|||||||
|
|
||||||
def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]:
|
def assignable_from(self, other: 'ParseResult.Value') -> Tuple[bool, str]:
|
||||||
if isinstance(other, ParseResult.IndirectValue):
|
if isinstance(other, ParseResult.IndirectValue):
|
||||||
other = other.value
|
if self.datatype == DataType.BYTE:
|
||||||
if other.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
if other.datatype == DataType.BYTE:
|
||||||
return True, ""
|
return True, ""
|
||||||
return False, "incompatible value for register assignment"
|
return False, "(unsigned) byte required"
|
||||||
|
if self.datatype == DataType.WORD:
|
||||||
|
if other.datatype in (DataType.BYTE, DataType.WORD):
|
||||||
|
return True, ""
|
||||||
|
return False, "(unsigned) byte required"
|
||||||
|
return False, "incompatible indirect value for register assignment"
|
||||||
if self.register == "SC":
|
if self.register == "SC":
|
||||||
if isinstance(other, ParseResult.IntegerValue) and other.value in (0, 1):
|
if isinstance(other, ParseResult.IntegerValue) and other.value in (0, 1):
|
||||||
return True, ""
|
return True, ""
|
||||||
@ -223,7 +239,16 @@ class ParseResult:
|
|||||||
return False, "register size mismatch"
|
return False, "register size mismatch"
|
||||||
if isinstance(other, ParseResult.StringValue) and self.register in REGISTER_BYTES:
|
if isinstance(other, ParseResult.StringValue) and self.register in REGISTER_BYTES:
|
||||||
return False, "string address requires 16 bits combined register"
|
return False, "string address requires 16 bits combined register"
|
||||||
if isinstance(other, (ParseResult.IntegerValue, ParseResult.FloatValue)):
|
if isinstance(other, ParseResult.IntegerValue):
|
||||||
|
if other.value is not None:
|
||||||
|
range_error = check_value_in_range(self.datatype, self.register, 1, other.value)
|
||||||
|
if range_error:
|
||||||
|
return False, range_error
|
||||||
|
return True, ""
|
||||||
|
if self.datatype == DataType.WORD:
|
||||||
|
return True, ""
|
||||||
|
return False, "cannot assign address to single register"
|
||||||
|
if isinstance(other, ParseResult.FloatValue):
|
||||||
range_error = check_value_in_range(self.datatype, self.register, 1, other.value)
|
range_error = check_value_in_range(self.datatype, self.register, 1, other.value)
|
||||||
if range_error:
|
if range_error:
|
||||||
return False, range_error
|
return False, range_error
|
||||||
@ -369,7 +394,7 @@ class ParseResult:
|
|||||||
return [self]
|
return [self]
|
||||||
statements = [] # type: List[ParseResult._AstNode]
|
statements = [] # type: List[ParseResult._AstNode]
|
||||||
for name, value in self.arguments:
|
for name, value in self.arguments:
|
||||||
assert name is not None, "call argument should have a parameter name assigned"
|
assert name is not None, "all call arguments should have a name or be matched on a named parameter"
|
||||||
assignment = parser.parse_assignment("{:s}={:s}".format(name, value))
|
assignment = parser.parse_assignment("{:s}={:s}".format(name, value))
|
||||||
assignment.lineno = self.lineno
|
assignment.lineno = self.lineno
|
||||||
statements.append(assignment)
|
statements.append(assignment)
|
||||||
@ -419,16 +444,18 @@ class Parser:
|
|||||||
with open(filename, "rU") as source:
|
with open(filename, "rU") as source:
|
||||||
sourcelines = source.readlines()
|
sourcelines = source.readlines()
|
||||||
# store all lines that aren't empty
|
# store all lines that aren't empty
|
||||||
# comments are kept (end-of-line comments are put on a separate line)
|
# comments are kept (end-of-line comments are stripped though)
|
||||||
lines = []
|
lines = []
|
||||||
for num, line in enumerate(sourcelines, start=1):
|
for num, line in enumerate(sourcelines, start=1):
|
||||||
line2 = line.strip()
|
line = line.rstrip()
|
||||||
if line2:
|
if line.lstrip().startswith(';'):
|
||||||
line, sep, comment = line.partition(";")
|
lines.append((num, line.lstrip()))
|
||||||
if comment:
|
else:
|
||||||
lines.append((num, "; " + comment.strip()))
|
line2, sep, comment = line.rpartition(';')
|
||||||
if line.rstrip():
|
if sep:
|
||||||
lines.append((num, line.rstrip()))
|
line = line2.rstrip()
|
||||||
|
if line:
|
||||||
|
lines.append((num, line))
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def parse(self) -> Optional[ParseResult]:
|
def parse(self) -> Optional[ParseResult]:
|
||||||
@ -759,21 +786,21 @@ class Parser:
|
|||||||
self.print_warning("warning: {}: Ignoring block without name and address.".format(self.cur_block.sourceref))
|
self.print_warning("warning: {}: Ignoring block without name and address.".format(self.cur_block.sourceref))
|
||||||
return None
|
return None
|
||||||
return self.cur_block
|
return self.cur_block
|
||||||
if line.startswith("var"):
|
if line.startswith(("var ", "var\t")):
|
||||||
self.parse_var_def(line)
|
self.parse_var_def(line)
|
||||||
elif line.startswith("const"):
|
elif line.startswith(("const ", "const\t")):
|
||||||
self.parse_const_def(line)
|
self.parse_const_def(line)
|
||||||
elif line.startswith("memory"):
|
elif line.startswith(("memory ", "memory\t")):
|
||||||
self.parse_memory_def(line, is_zp_block)
|
self.parse_memory_def(line, is_zp_block)
|
||||||
elif line.startswith("subx"):
|
elif line.startswith(("subx ", "subx\t")):
|
||||||
if is_zp_block:
|
if is_zp_block:
|
||||||
raise self.PError("ZP block cannot contain subroutines")
|
raise self.PError("ZP block cannot contain subroutines")
|
||||||
self.parse_subx_def(line)
|
self.parse_subx_def(line)
|
||||||
elif line.startswith(("asminclude", "asmbinary")):
|
elif line.startswith(("asminclude ", "asminclude\t", "asmbinary ", "asmbinary\t")):
|
||||||
if is_zp_block:
|
if is_zp_block:
|
||||||
raise self.PError("ZP block cannot contain assembler directives")
|
raise self.PError("ZP block cannot contain assembler directives")
|
||||||
self.cur_block.statements.append(self.parse_asminclude(line))
|
self.cur_block.statements.append(self.parse_asminclude(line))
|
||||||
elif line.startswith("asm"):
|
elif line.startswith(("asm ", "asm\t")):
|
||||||
if is_zp_block:
|
if is_zp_block:
|
||||||
raise self.PError("ZP block cannot contain code statements")
|
raise self.PError("ZP block cannot contain code statements")
|
||||||
self.prev_line()
|
self.prev_line()
|
||||||
@ -896,15 +923,18 @@ class Parser:
|
|||||||
return varname, datatype, length, matrix_dimensions, valuetext
|
return varname, datatype, length, matrix_dimensions, valuetext
|
||||||
|
|
||||||
def parse_statement(self, line: str) -> ParseResult._AstNode:
|
def parse_statement(self, line: str) -> ParseResult._AstNode:
|
||||||
# check if we have a subroutine call using () syntax
|
match = re.match(r"(?P<goto>goto\s+)?(?P<subname>[\S]+?)\s*(?P<fcall>[!]?)\s*(\((?P<arguments>.*)\))?\s*$", line)
|
||||||
match = re.match(r"^(?P<subname>[\w\.]+)\s*(?P<fcall>[!]?)\s*\((?P<params>.*)\)\s*$", line)
|
|
||||||
if match:
|
if match:
|
||||||
|
# subroutine or goto call
|
||||||
|
is_goto = bool(match.group("goto"))
|
||||||
|
preserve = not bool(match.group("fcall"))
|
||||||
subname = match.group("subname")
|
subname = match.group("subname")
|
||||||
fcall = "f" if match.group("fcall") else ""
|
arguments = match.group("arguments")
|
||||||
param_str = match.group("params")
|
if is_goto:
|
||||||
# turn this into "[f]call subname parameters" so it will be parsed below
|
return self.parse_call_or_goto(subname, arguments, preserve, True)
|
||||||
line = "{:s}call {:s} {:s}".format(fcall, subname, param_str)
|
elif arguments or match.group(4):
|
||||||
if line.startswith("return"):
|
return self.parse_call_or_goto(subname, arguments, preserve, False)
|
||||||
|
if line == "return" or line.startswith(("return ", "return\t")):
|
||||||
return self.parse_return(line)
|
return self.parse_return(line)
|
||||||
elif line.endswith(("++", "--")):
|
elif line.endswith(("++", "--")):
|
||||||
incr = line.endswith("++")
|
incr = line.endswith("++")
|
||||||
@ -912,12 +942,6 @@ class Parser:
|
|||||||
if isinstance(what, ParseResult.IntegerValue):
|
if isinstance(what, ParseResult.IntegerValue):
|
||||||
raise self.PError("cannot in/decrement a constant value")
|
raise self.PError("cannot in/decrement a constant value")
|
||||||
return ParseResult.IncrDecrStmt(what, 1 if incr else -1)
|
return ParseResult.IncrDecrStmt(what, 1 if incr else -1)
|
||||||
elif line.startswith("call"):
|
|
||||||
return self.parse_call_or_go(line, "call")
|
|
||||||
elif line.startswith("fcall"):
|
|
||||||
return self.parse_call_or_go(line, "fcall")
|
|
||||||
elif line.startswith("go"):
|
|
||||||
return self.parse_call_or_go(line, "go")
|
|
||||||
else:
|
else:
|
||||||
# perhaps it is an assignment statment
|
# perhaps it is an assignment statment
|
||||||
lhs, sep, rhs = line.partition("=")
|
lhs, sep, rhs = line.partition("=")
|
||||||
@ -925,13 +949,10 @@ class Parser:
|
|||||||
return self.parse_assignment(line)
|
return self.parse_assignment(line)
|
||||||
raise self.PError("invalid statement")
|
raise self.PError("invalid statement")
|
||||||
|
|
||||||
def parse_call_or_go(self, line: str, what: str) -> ParseResult.CallStmt:
|
def parse_call_or_goto(self, targetstr: str, argumentstr: str, preserve_regs=True, is_goto=False) -> ParseResult.CallStmt:
|
||||||
args = line.split(maxsplit=2)
|
argumentstr = argumentstr.strip() if argumentstr else ""
|
||||||
arguments = None
|
arguments = None
|
||||||
if len(args) == 2:
|
if argumentstr:
|
||||||
targetstr, argumentstr, = args[1], ""
|
|
||||||
elif len(args) == 3:
|
|
||||||
targetstr, argumentstr = args[1], args[2]
|
|
||||||
arguments = []
|
arguments = []
|
||||||
for part in argumentstr.split(','):
|
for part in argumentstr.split(','):
|
||||||
pname, sep, pvalue = part.partition('=')
|
pname, sep, pvalue = part.partition('=')
|
||||||
@ -941,8 +962,6 @@ class Parser:
|
|||||||
arguments.append((pname, pvalue))
|
arguments.append((pname, pvalue))
|
||||||
else:
|
else:
|
||||||
arguments.append((None, pname))
|
arguments.append((None, pname))
|
||||||
else:
|
|
||||||
raise self.PError("invalid call/go arguments")
|
|
||||||
target = None # type: ParseResult.Value
|
target = None # type: ParseResult.Value
|
||||||
if targetstr[0] == '[' and targetstr[-1] == ']':
|
if targetstr[0] == '[' and targetstr[-1] == ']':
|
||||||
# indirect call to address in register pair or memory location
|
# indirect call to address in register pair or memory location
|
||||||
@ -961,9 +980,9 @@ class Parser:
|
|||||||
_, symbol = self.cur_block.lookup(targetstr)
|
_, symbol = self.cur_block.lookup(targetstr)
|
||||||
if isinstance(symbol, SubroutineDef):
|
if isinstance(symbol, SubroutineDef):
|
||||||
# verify subroutine arguments
|
# verify subroutine arguments
|
||||||
if arguments is not None and len(arguments) != len(symbol.parameters):
|
if len(arguments or []) != len(symbol.parameters):
|
||||||
raise self.PError("invalid number of arguments ({:d}, expected {:d})"
|
raise self.PError("invalid number of arguments ({:d}, expected {:d})"
|
||||||
.format(len(arguments), len(symbol.parameters)))
|
.format(len(arguments or []), len(symbol.parameters)))
|
||||||
args_with_pnames = []
|
args_with_pnames = []
|
||||||
for i, (argname, value) in enumerate(arguments or []):
|
for i, (argname, value) in enumerate(arguments or []):
|
||||||
pname, preg = symbol.parameters[i]
|
pname, preg = symbol.parameters[i]
|
||||||
@ -974,15 +993,18 @@ class Parser:
|
|||||||
argname = preg
|
argname = preg
|
||||||
args_with_pnames.append((argname, value))
|
args_with_pnames.append((argname, value))
|
||||||
arguments = args_with_pnames
|
arguments = args_with_pnames
|
||||||
|
else:
|
||||||
|
if arguments:
|
||||||
|
raise self.PError("call cannot take any arguments here, use a subroutine for that")
|
||||||
|
if arguments:
|
||||||
|
# verify that all arguments have gotten a name
|
||||||
|
if any(not a[0] for a in arguments):
|
||||||
|
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)):
|
||||||
if what == "go":
|
if is_goto:
|
||||||
return ParseResult.CallStmt(self.sourceref.line, target, address=address, is_goto=True)
|
return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments, is_goto=True)
|
||||||
elif what == "call":
|
|
||||||
return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments)
|
|
||||||
elif what == "fcall":
|
|
||||||
return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments, preserve_regs=False)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("invalid what")
|
return ParseResult.CallStmt(self.sourceref.line, target, address=address, arguments=arguments, preserve_regs=preserve_regs)
|
||||||
else:
|
else:
|
||||||
raise TypeError("target should be a Value", target)
|
raise TypeError("target should be a Value", target)
|
||||||
|
|
||||||
@ -1085,12 +1107,15 @@ class Parser:
|
|||||||
else:
|
else:
|
||||||
raise self.PError("invalid statement")
|
raise self.PError("invalid statement")
|
||||||
|
|
||||||
def parse_expression(self, text: str) -> ParseResult.Value:
|
def parse_expression(self, text: str, is_indirect=False) -> ParseResult.Value:
|
||||||
# parse an expression into whatever it is (primitive value, register, memory, register, etc)
|
# parse an expression into whatever it is (primitive value, register, memory, register, etc)
|
||||||
|
# @todo only numeric expressions supported for now
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
if not text:
|
if not text:
|
||||||
raise self.PError("value expected")
|
raise self.PError("value expected")
|
||||||
if text[0] == '#':
|
if text[0] == '#':
|
||||||
|
if is_indirect:
|
||||||
|
raise self.PError("using the address-of something in an indirect value makes no sense")
|
||||||
# take the pointer (memory address) from the thing that follows this
|
# take the pointer (memory address) from the thing that follows this
|
||||||
expression = self.parse_expression(text[1:])
|
expression = self.parse_expression(text[1:])
|
||||||
if isinstance(expression, ParseResult.StringValue):
|
if isinstance(expression, ParseResult.StringValue):
|
||||||
@ -1175,11 +1200,16 @@ class Parser:
|
|||||||
if typestr in ("byte", "word", "float"):
|
if typestr in ("byte", "word", "float"):
|
||||||
type_modifier, type_len, _ = self.get_datatype(sep + typestr)
|
type_modifier, type_len, _ = self.get_datatype(sep + typestr)
|
||||||
indirect = indirect2
|
indirect = indirect2
|
||||||
expr = self.parse_expression(indirect)
|
expr = self.parse_expression(indirect, True)
|
||||||
if not isinstance(expr, (ParseResult.IntegerValue, ParseResult.MemMappedValue, ParseResult.RegisterValue)):
|
if not isinstance(expr, (ParseResult.IntegerValue, ParseResult.MemMappedValue, ParseResult.RegisterValue)):
|
||||||
raise self.PError("only integers, memmapped vars, and registers can be used in an indirect value")
|
raise self.PError("only integers, memmapped vars, and registers can be used in an indirect value")
|
||||||
|
if type_modifier is None:
|
||||||
|
if isinstance(expr, (ParseResult.RegisterValue, ParseResult.MemMappedValue)):
|
||||||
|
type_modifier = expr.datatype
|
||||||
|
else:
|
||||||
|
type_modifier = DataType.BYTE
|
||||||
if isinstance(expr, ParseResult.IntegerValue):
|
if isinstance(expr, ParseResult.IntegerValue):
|
||||||
if type_modifier not in (None, DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
if type_modifier not in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
raise self.PError("invalid type modifier for the value's datatype")
|
raise self.PError("invalid type modifier for the value's datatype")
|
||||||
elif isinstance(expr, ParseResult.MemMappedValue):
|
elif isinstance(expr, ParseResult.MemMappedValue):
|
||||||
if type_modifier and expr.datatype != type_modifier:
|
if type_modifier and expr.datatype != type_modifier:
|
||||||
|
@ -262,7 +262,7 @@ class SymbolTable:
|
|||||||
return self, getattr(math, nameparts[0])
|
return self, getattr(math, nameparts[0])
|
||||||
elif nameparts[0] in BUILTIN_SYMBOLS:
|
elif nameparts[0] in BUILTIN_SYMBOLS:
|
||||||
return self, getattr(builtins, nameparts[0])
|
return self, getattr(builtins, nameparts[0])
|
||||||
raise SymbolError("undefined symbol '{:s}'".format(nameparts[0]))
|
raise SymbolError("undefined symbol '{:s}'".format(nameparts[0])) from None
|
||||||
# start from toplevel namespace:
|
# start from toplevel namespace:
|
||||||
scope = self
|
scope = self
|
||||||
while scope.parent:
|
while scope.parent:
|
||||||
@ -272,7 +272,7 @@ class SymbolTable:
|
|||||||
scope = scope.symbols[namepart] # type: ignore
|
scope = scope.symbols[namepart] # type: ignore
|
||||||
assert scope.name == namepart
|
assert scope.name == namepart
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise SymbolError("undefined block '{:s}'".format(namepart))
|
raise SymbolError("undefined block '{:s}'".format(namepart)) from None
|
||||||
if isinstance(scope, SymbolTable):
|
if isinstance(scope, SymbolTable):
|
||||||
return scope.lookup(nameparts[-1])
|
return scope.lookup(nameparts[-1])
|
||||||
else:
|
else:
|
||||||
@ -445,11 +445,11 @@ class SymbolTable:
|
|||||||
|
|
||||||
|
|
||||||
class EvalSymbolDict(dict):
|
class EvalSymbolDict(dict):
|
||||||
def __init__(self, symboltable: SymbolTable, ppsymbols: SymbolTable, constants: bool=True) -> None:
|
def __init__(self, symboltable: SymbolTable, ppsymbols: SymbolTable, constant: bool=True) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._symboltable = symboltable
|
self._symboltable = symboltable
|
||||||
self._constants = constants
|
|
||||||
self._ppsymbols = ppsymbols
|
self._ppsymbols = ppsymbols
|
||||||
|
self._is_constant = constant
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return self.__getitem__(name)
|
return self.__getitem__(name)
|
||||||
@ -471,17 +471,17 @@ class EvalSymbolDict(dict):
|
|||||||
if self._ppsymbols:
|
if self._ppsymbols:
|
||||||
return self._ppsymbols.as_eval_dict(None)[name]
|
return self._ppsymbols.as_eval_dict(None)[name]
|
||||||
raise SymbolError("undefined symbol '{:s}'".format(name)) from None
|
raise SymbolError("undefined symbol '{:s}'".format(name)) from None
|
||||||
if self._constants:
|
if self._is_constant:
|
||||||
if isinstance(symbol, ConstantDef):
|
if isinstance(symbol, ConstantDef):
|
||||||
return symbol.value
|
return symbol.value
|
||||||
elif isinstance(symbol, VariableDef):
|
elif isinstance(symbol, VariableDef):
|
||||||
return symbol.value
|
raise SymbolError("can't reference a variable inside a (constant) expression")
|
||||||
elif inspect.isbuiltin(symbol):
|
elif inspect.isbuiltin(symbol):
|
||||||
return symbol
|
return symbol
|
||||||
elif isinstance(symbol, SymbolTable):
|
elif isinstance(symbol, SymbolTable):
|
||||||
return symbol.as_eval_dict(self._ppsymbols)
|
return symbol.as_eval_dict(self._ppsymbols)
|
||||||
elif isinstance(symbol, LabelDef):
|
elif isinstance(symbol, (LabelDef, SubroutineDef)):
|
||||||
raise SymbolError("can't reference a label here")
|
raise SymbolError("can't reference a label or subroutine inside a (constant) expression")
|
||||||
else:
|
else:
|
||||||
raise SymbolError("invalid symbol type referenced " + repr(symbol))
|
raise SymbolError("invalid symbol type referenced " + repr(symbol))
|
||||||
else:
|
else:
|
||||||
|
@ -158,7 +158,8 @@ The syntax "[address]" means: the contents of the memory at address.
|
|||||||
By default, if not otherwise known, a single byte is assumed. You can add the ".byte" or ".word" or ".float" suffix
|
By default, if not otherwise known, a single byte is assumed. You can add the ".byte" or ".word" or ".float" suffix
|
||||||
to make it clear what data type the address points to.
|
to make it clear what data type the address points to.
|
||||||
|
|
||||||
Everything after a semicolon ';' is a comment and is ignored, however the comment is copied into the resulting assembly source code.
|
Everything after a semicolon ';' is a comment and is ignored, however the comment (if it is the only thing
|
||||||
|
on the line) is copied into the resulting assembly source code.
|
||||||
|
|
||||||
|
|
||||||
FLOW CONTROL
|
FLOW CONTROL
|
||||||
@ -349,28 +350,20 @@ ISOLATION (register preservation when calling subroutines): @todo isolation
|
|||||||
SUBROUTINE CALLS
|
SUBROUTINE CALLS
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
CALL and FCALL:
|
You call a subroutine like this:
|
||||||
They are just inserting a call to the specified location or subroutine.
|
subroutinename[!] ( [arguments...] )
|
||||||
[F]CALL: calls subroutine and continue afterwards ('gosub'):
|
|
||||||
[f]call <subroutine> / <label> / <address> / `[`indirect-pointer`]` [arguments...]
|
|
||||||
|
|
||||||
A 'call' preserves all registers when doing the procedure call and restores them afterwards.
|
Normally, the registers are preserved when calling the subroutine and restored on return.
|
||||||
'fcall' (fast call) doesn't preserve registers, so generates code that is a lot faster.
|
If you add a '!' after the name, no register preserving is done and the call essentially
|
||||||
It's basically one jmp or jsr instruction. It can clobber register values because of this.
|
is just a single JSR instruction.
|
||||||
If you provide arguments (not required) these will be matched to the subroutine's parameters.
|
Arguments should match the subroutine definition. You are allowed to omit the parameter names.
|
||||||
If you don't provide arguments, it is assumed you have prepared the correct registers etc yourself.
|
If no definition is available (because you're directly calling memory or a label or something else),
|
||||||
|
you can freely add arguments (but in this case they all have to be named).
|
||||||
|
|
||||||
|
To jump to a subroutine (without returning), prefix the subroutine call with the word 'goto'.
|
||||||
|
Unlike gotos in other languages, here it take arguments as well, because it
|
||||||
|
essentially is the same as calling a subroutine and only doing something different when it's finished.
|
||||||
|
|
||||||
The following contemporary syntax to call a subroutine is also available:
|
|
||||||
subroutine `(` [arguments...] `)`
|
|
||||||
subroutine! `(` [arguments...] `)`
|
|
||||||
These are understood as: "call subroutine arguments" and "fcall subroutine arguments" respectively.
|
|
||||||
You can only call a subroutine or label this way. This syntax cannot be used
|
|
||||||
to call a memory address or variable, you have to use the call statement for that.
|
|
||||||
|
|
||||||
GO:
|
|
||||||
'go' continues execution with the specified routine or address and doesn't retuurn (it is a 'goto'):
|
|
||||||
go <subroutine> / <label> / <address> / [indirect-pointer]
|
|
||||||
|
|
||||||
|
|
||||||
@todo support call non-register args (variable parameter passing)
|
@todo support call non-register args (variable parameter passing)
|
||||||
|
@ -16,19 +16,27 @@
|
|||||||
const .text constt = "derp"
|
const .text constt = "derp"
|
||||||
|
|
||||||
subx sub1 () -> (X?) = $ffdd
|
subx sub1 () -> (X?) = $ffdd
|
||||||
|
subx sub2 (A) -> (Y?) = $eecc
|
||||||
|
|
||||||
|
|
||||||
bar
|
bar
|
||||||
go sub1
|
goto sub1
|
||||||
go bar
|
goto sub2 (1 )
|
||||||
go [AX]
|
goto bar ()
|
||||||
go [var1]
|
goto [AX]
|
||||||
go [mem1]
|
goto [AX] ()
|
||||||
go [#mem1] ; @todo error: indirection of address-of makes no sense, should have outputted jmp mem1 instead of jmp(mem1)
|
goto [var1]
|
||||||
go [$c2.word]
|
goto [var1] () ; comment
|
||||||
go [$c2dd.word]
|
goto [mem1] ; comment
|
||||||
go $c000
|
goto [mem1] ()
|
||||||
go $c2
|
goto [$c2.word]
|
||||||
|
goto [$c2.word] ()
|
||||||
|
goto [$c2dd.word]
|
||||||
|
goto [$c2dd.word] ( )
|
||||||
|
goto $c000
|
||||||
|
goto $c000 ( )
|
||||||
|
goto $c2
|
||||||
|
goto $c2()
|
||||||
|
|
||||||
asm {
|
asm {
|
||||||
nop
|
nop
|
||||||
@ -37,16 +45,16 @@ bar
|
|||||||
nop
|
nop
|
||||||
}
|
}
|
||||||
|
|
||||||
fcall sub1
|
sub1!()
|
||||||
fcall bar
|
sub2!(11)
|
||||||
fcall [XY]
|
bar!()
|
||||||
fcall [var1]
|
[XY] ! ()
|
||||||
fcall [mem1]
|
[var1] !()
|
||||||
fcall [#mem1] ; @todo error: indirection of address-of makes no sense
|
[mem1]!()
|
||||||
fcall [$c2.word]
|
[$c2.word]!()
|
||||||
fcall [$c2dd.word]
|
[$c2dd.word]!()
|
||||||
fcall $c000
|
$c000!()
|
||||||
fcall $c2
|
$c2!()
|
||||||
|
|
||||||
asm {
|
asm {
|
||||||
nop
|
nop
|
||||||
@ -55,16 +63,16 @@ bar
|
|||||||
nop
|
nop
|
||||||
}
|
}
|
||||||
|
|
||||||
call sub1
|
sub1()
|
||||||
call bar
|
sub2(11)
|
||||||
call [AX]
|
bar ()
|
||||||
call [var1]
|
[AX]()
|
||||||
call [mem1]
|
[var1] ( )
|
||||||
call [#mem1] ; @todo error: indirection of address-of makes no sense
|
[mem1] ()
|
||||||
call [$c2.word]
|
[$c2.word]()
|
||||||
call [$c2dd.word]
|
[$c2dd.word]()
|
||||||
call $c000
|
$c000()
|
||||||
call $c2
|
$c2()
|
||||||
|
|
||||||
|
|
||||||
asm {
|
asm {
|
||||||
@ -74,16 +82,15 @@ bar
|
|||||||
nop
|
nop
|
||||||
}
|
}
|
||||||
|
|
||||||
call constw
|
constw()
|
||||||
call sub1
|
sub1()
|
||||||
call main.start
|
main.start()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
~ main {
|
~ main {
|
||||||
|
|
||||||
start
|
start
|
||||||
call foo.bar
|
foo.bar()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ clobberzp
|
|||||||
var .word initword1b = true
|
var .word initword1b = true
|
||||||
var .word initword2 = false
|
var .word initword2 = false
|
||||||
var .word initword3 = 9876.554321
|
var .word initword3 = 9876.554321
|
||||||
var .word initword4 = initword3
|
|
||||||
var .word initword5 = 20
|
var .word initword5 = 20
|
||||||
var .float uninitfloat
|
var .float uninitfloat
|
||||||
var .float initfloat1 = 0
|
var .float initfloat1 = 0
|
||||||
@ -76,8 +75,7 @@ clobberzp
|
|||||||
|
|
||||||
; memory-mapped variables
|
; memory-mapped variables
|
||||||
memory membyte1 = $cf01
|
memory membyte1 = $cf01
|
||||||
memory .byte membyte2 = $c2
|
memory .byte membyte2 = $c222
|
||||||
memory .byte membyte3 = initbyte2
|
|
||||||
memory .word memword1 = $cf03
|
memory .word memword1 = $cf03
|
||||||
memory .float memfloat = $cf04
|
memory .float memfloat = $cf04
|
||||||
memory .array(10 ) membytes = $cf05
|
memory .array(10 ) membytes = $cf05
|
||||||
@ -109,7 +107,6 @@ clobberzp
|
|||||||
var .word vmemaddr4 = #membytes
|
var .word vmemaddr4 = #membytes
|
||||||
var .word vmemaddr5 = #memwords
|
var .word vmemaddr5 = #memwords
|
||||||
var .word vmemaddr6 = #memmatrix
|
var .word vmemaddr6 = #memmatrix
|
||||||
var .word vmemaddr7 = vmemaddr1
|
|
||||||
var .word vmemaddr8 = 100*sin(cbyte1)
|
var .word vmemaddr8 = 100*sin(cbyte1)
|
||||||
var .word vmemaddr9 = cword2+$5432
|
var .word vmemaddr9 = cword2+$5432
|
||||||
var .word vmemaddr10 = cfloat2b
|
var .word vmemaddr10 = cfloat2b
|
||||||
@ -144,17 +141,16 @@ start
|
|||||||
A = false
|
A = false
|
||||||
A = 255
|
A = 255
|
||||||
A = X
|
A = X
|
||||||
|
A = [$99]
|
||||||
|
A = [$c020.byte]
|
||||||
A = [$c020]
|
A = [$c020]
|
||||||
A = [#membyte2]
|
A = cbyte3
|
||||||
A = membyte2
|
A = membyte2
|
||||||
A = [membyte2] ; @todo error, invalid rvalue, use membyte without indirect?
|
A = uninitbyte1
|
||||||
A = [membyte2.byte] ; @todo error, "
|
;A = [membyte2] ; @todo error, invalid rvalue, use membyte without indirect?
|
||||||
A = expr_byte1b ; @todo ok
|
;A = [membyte2.byte] ; @todo error, "
|
||||||
;A = #expr_byte1b ; @todo cannot assign address to byte, correct error
|
|
||||||
;A = cbyte3 ; @todo fix assignment to lda #cybte3
|
|
||||||
;A = [cbyte3] ; @todo error invalid rvalue
|
;A = [cbyte3] ; @todo error invalid rvalue
|
||||||
A = initbytea0
|
;A = [initbytea0] ; @todo error, invalid rvalue, use initbytea0 without indirect?
|
||||||
A = [initbytea0] ; @todo error, invalid rvalue, use initbytea0 without indirect?
|
|
||||||
|
|
||||||
|
|
||||||
XY = 0
|
XY = 0
|
||||||
@ -165,43 +161,67 @@ start
|
|||||||
XY = true
|
XY = true
|
||||||
XY = false
|
XY = false
|
||||||
XY = text
|
XY = text
|
||||||
|
XY = cbyte3
|
||||||
|
XY = cword2
|
||||||
|
XY = uninitbyte1
|
||||||
XY = "text-immediate"
|
XY = "text-immediate"
|
||||||
AY = "text-immediate"
|
AY = "text-immediate"
|
||||||
AX = #"text-immediate" ; equivalent to simply assigning the string directly
|
AX = #"text-immediate" ; equivalent to simply assigning the string directly
|
||||||
AX = # "text-immediate" ; equivalent to simply assigning the string directly
|
AX = # "text-immediate" ; equivalent to simply assigning the string directly
|
||||||
|
AX = ctext3
|
||||||
AX = ""
|
AX = ""
|
||||||
AX = XY
|
AX = XY
|
||||||
AX = Y
|
AX = Y
|
||||||
;XY = [membyte2] ; @todo ok pad
|
XY = [membyte2] ; @todo indirection error?
|
||||||
;XY = [membyte2.byte] ; @todo ok pad
|
XY = [membyte2.byte] ; @todo indirection error?
|
||||||
;XY = membyte2 ; @todo ok pad
|
XY = membyte2
|
||||||
;XY = #membyte2 ; @todo ok
|
XY = #membyte2
|
||||||
;XY = [memword1] ; @todo ok
|
XY = [memword1] ; @todo indirection error?
|
||||||
;XY = [memword1.byte] ; @todo ok pad
|
XY = [memword1.word] ; @todo indirection error?
|
||||||
;XY = [memword1.word] ; @todo ok
|
XY = memword1
|
||||||
XY = sin ; @todo ok
|
XY = sin
|
||||||
|
XY = #sin
|
||||||
|
|
||||||
|
|
||||||
|
[$c000] = A
|
||||||
[$c000] = 255
|
[$c000] = 255
|
||||||
[$c000] = '@'
|
[$c000] = '@'
|
||||||
[$c000] = 1.2345
|
|
||||||
[$c000] = true
|
[$c000] = true
|
||||||
[$c000] = false
|
[$c000] = false
|
||||||
|
[$c000] = cbyte3
|
||||||
|
[$c000] = uninitbyte1
|
||||||
|
[$c000] = [membyte2] ; @todo indirection error?
|
||||||
|
[$c000] = [membyte2.byte] ; @todo indirection error?
|
||||||
|
[$c000] = membyte2
|
||||||
|
|
||||||
|
[$c000.word] = A
|
||||||
|
[$c000.word] = AX
|
||||||
|
[$c000.word] = cbyte3
|
||||||
|
[$c000.word] = cword2
|
||||||
|
[$c000.word] = ctext3
|
||||||
[$c000.word] = 65535
|
[$c000.word] = 65535
|
||||||
[$c000.word] = 456.66
|
|
||||||
[$c000.word] = "text"
|
[$c000.word] = "text"
|
||||||
[$c000.word] = ""
|
[$c000.word] = ""
|
||||||
|
[$c000.word] = uninitbyte1
|
||||||
|
[$c000.word] = [membyte2] ; @todo indirection error?
|
||||||
|
[$c000.word] = [membyte2.byte] ; @todo indirection error?
|
||||||
|
[$c000.word] = membyte2
|
||||||
|
[$c000.word] = #membyte2
|
||||||
|
[$c000.word] = [memword1] ; @todo indirection error?
|
||||||
|
[$c000.word] = [memword1.word] ; @todo indirection error?
|
||||||
|
[$c000.word] = memword1
|
||||||
[$c000.float] = 65535
|
[$c000.float] = 65535
|
||||||
[$c000.float] = 456.66
|
[$c000.float] = 456.66
|
||||||
[$c000.float] = 1.70141183e+38
|
[$c000.float] = 1.70141183e+38
|
||||||
[$c000.word] = AX
|
[$c000.float] = cbyte3
|
||||||
|
[$c000.float] = cword2
|
||||||
|
|
||||||
[$c001] = [$c002]
|
[$c001] = [$c002]
|
||||||
[$c111.word] = [$c222]
|
[$c111.word] = [$c222]
|
||||||
[$c112.word] = [$c223.byte]
|
[$c112.word] = [$c223.byte]
|
||||||
[$c222.word] = [$c333.word]
|
[$c222.word] = [$c333.word]
|
||||||
[$c333] = sin ; @todo error byte required
|
[$c333.word] = sin
|
||||||
[$c333.word] = sin ; @todo ok
|
[$c333.word] = #sin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SC = 0
|
SC = 0
|
||||||
@ -215,36 +235,59 @@ start
|
|||||||
initbyte1 = 1.234
|
initbyte1 = 1.234
|
||||||
initbyte1 = '@'
|
initbyte1 = '@'
|
||||||
initbyte1 = A
|
initbyte1 = A
|
||||||
|
initbyte1 = cbyte3
|
||||||
uninitword = 99
|
uninitword = 99
|
||||||
uninitword = 5.6778
|
uninitword = 5.6778
|
||||||
uninitword = "test"
|
uninitword = "test"
|
||||||
uninitword = '@'
|
uninitword = '@'
|
||||||
uninitword = A
|
uninitword = A
|
||||||
uninitword = XY
|
uninitword = XY
|
||||||
|
uninitword = ctext3
|
||||||
|
initword1 = cbyte3
|
||||||
|
initword1 = cword2
|
||||||
initfloat1 = 99
|
initfloat1 = 99
|
||||||
initfloat1 = 9.8765
|
initfloat1 = 9.8765
|
||||||
initfloat1 = '@'
|
initfloat1 = '@'
|
||||||
|
initfloat1 = cbyte3
|
||||||
|
initfloat1 = cword2
|
||||||
uninitfloat = 99
|
uninitfloat = 99
|
||||||
uninitfloat = 9.8765
|
uninitfloat = 9.8765
|
||||||
uninitfloat = '@'
|
uninitfloat = '@'
|
||||||
;uninitfloat = A ; @todo support this
|
initword1 = sin
|
||||||
; uninitfloat = XY ; @todo support this
|
initword1 = #sin
|
||||||
initword1 = sin ; @todo ok
|
|
||||||
|
membyte1 = A
|
||||||
|
membyte1 = cbyte3
|
||||||
|
memword1 = A
|
||||||
|
memword1 = AX
|
||||||
|
memword1 = cbyte3
|
||||||
|
memword1 = cword2
|
||||||
|
memword1 = ctext3
|
||||||
|
|
||||||
|
|
||||||
membyte1 = 22
|
membyte1 = 22
|
||||||
memword1 = 2233
|
memword1 = 2233
|
||||||
memfloat = 3.4567
|
memfloat = 3.4567
|
||||||
;[membyte1] = 33 ; @todo error, invalid lvalue, use without []
|
;[membyte1] = 33 ; @todo error, invalid lvalue, use without []
|
||||||
[memword1] = 4444
|
;[memword1] = 4444 ; @todo error ^
|
||||||
;[memword1] = [AX] ; @todo error, only address allowed in []
|
;[memword1] = [AX] ; @todo error, only address allowed in []
|
||||||
[memfloat] = 5.5566
|
;[memfloat] = 5.5566 ; @todo error ^
|
||||||
memword1 = sin ; @todo ok
|
memword1 = sin
|
||||||
|
memword1 = #sin
|
||||||
|
|
||||||
membyte1 = A
|
membyte1 = A
|
||||||
memword1 = A
|
memword1 = A
|
||||||
memword1 = XY
|
memword1 = XY
|
||||||
;memfloat = A ; @todo support this
|
memfloat = cbyte3
|
||||||
;memfloat = XY ; @todo support this
|
memfloat = cword2
|
||||||
|
|
||||||
|
; @todo float assignments that require ROM functions:
|
||||||
|
; memfloat = Y
|
||||||
|
; memfloat = XY
|
||||||
|
; uninitfloat = Y
|
||||||
|
; uninitfloat = XY
|
||||||
|
; initfloat2 = Y
|
||||||
|
; initfloat2 = XY
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ start
|
|||||||
|
|
||||||
|
|
||||||
start
|
start
|
||||||
|
; assign some float values to the memory
|
||||||
AY = #flt_pi
|
AY = #flt_pi
|
||||||
[some_address] = # flt_pi
|
[some_address] = # flt_pi
|
||||||
[some_address] = # flt_pi
|
[some_address] = # flt_pi
|
||||||
@ -65,6 +66,8 @@ start
|
|||||||
[some_address.word] = #flt_pi
|
[some_address.word] = #flt_pi
|
||||||
[$c000.word] = # flt_pi
|
[$c000.word] = # flt_pi
|
||||||
|
|
||||||
|
; print some floating points from source and compare them with ROM
|
||||||
|
|
||||||
c64.MOVFM!(#flt_pi)
|
c64.MOVFM!(#flt_pi)
|
||||||
c64.FPRINTLN!()
|
c64.FPRINTLN!()
|
||||||
c64.MOVFM!(#c64.FL_PIVAL)
|
c64.MOVFM!(#c64.FL_PIVAL)
|
||||||
|
@ -74,8 +74,8 @@ _loop block2.zpw1 ++
|
|||||||
Y--
|
Y--
|
||||||
[$d020]--
|
[$d020]--
|
||||||
[block2.zpw2] = 99
|
[block2.zpw2] = 99
|
||||||
call fidget.subroutine
|
fidget.subroutine()
|
||||||
go _loop
|
goto _loop
|
||||||
return 155,2,%00000101 ; will end up in A, X, Y
|
return 155,2,%00000101 ; will end up in A, X, Y
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +102,8 @@ somelabel1
|
|||||||
nop
|
nop
|
||||||
}
|
}
|
||||||
|
|
||||||
go somelabel1
|
goto somelabel1
|
||||||
go block2.somelabel1222
|
goto block2.somelabel1222
|
||||||
A=X=Y=A=X=Y=A=X=Y=99
|
A=X=Y=A=X=Y=A=X=Y=99
|
||||||
[$d020]=[$d021]=[$d020]=[$d021]=55
|
[$d020]=[$d021]=[$d020]=[$d021]=55
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import "c64lib" ; searched in several locations and with .ill file
|
|||||||
var .text hello2 = "@@\f\b\n\r\t@@"
|
var .text hello2 = "@@\f\b\n\r\t@@"
|
||||||
|
|
||||||
start
|
start
|
||||||
call global2.make_screen_black
|
global2.make_screen_black()
|
||||||
|
|
||||||
A='?'
|
A='?'
|
||||||
[$d020] = '?'
|
[$d020] = '?'
|
||||||
@ -46,9 +46,7 @@ start
|
|||||||
X=A
|
X=A
|
||||||
A='\xf2'
|
A='\xf2'
|
||||||
X=A
|
X=A
|
||||||
A='A'
|
c64.CHROUT('A')
|
||||||
call c64.CHROUT ;(A)
|
|
||||||
call c64.CHROUT ;(char=66)
|
|
||||||
A='\f'
|
A='\f'
|
||||||
X=A
|
X=A
|
||||||
A='\b'
|
A='\b'
|
||||||
@ -58,18 +56,13 @@ start
|
|||||||
A='\r'
|
A='\r'
|
||||||
X=A
|
X=A
|
||||||
A='\t'
|
A='\t'
|
||||||
X=A
|
|
||||||
call c64.CHROUT ;(foo=A)
|
|
||||||
A='0'
|
A='0'
|
||||||
call c64.CHROUT ;('0')
|
c64.CHROUT('0')
|
||||||
A='1'
|
c64.CHROUT('1')
|
||||||
call c64.CHROUT ;(49)
|
c64.CHROUT('2')
|
||||||
A='2'
|
|
||||||
call c64.CHROUT
|
|
||||||
XY = hello
|
XY = hello
|
||||||
call c64util.print_string
|
c64util.print_string()
|
||||||
A='!'
|
goto c64.CHROUT('!')
|
||||||
go c64.CHROUT
|
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
output prg,sys ; create a c-64 program with basic SYS call to launch it
|
output prg,sys ; create a c-64 program with basic SYS to() launch it
|
||||||
|
|
||||||
import "c64lib.ill"
|
import "c64lib.ill"
|
||||||
|
|
||||||
@ -9,67 +9,58 @@ output prg,sys ; create a c-64 program with basic SYS call to launch it
|
|||||||
const .word BORDER = $d020
|
const .word BORDER = $d020
|
||||||
|
|
||||||
start
|
start
|
||||||
fcall c64util.print_pimmediate ; this prints the pstring immediately following it
|
c64util.print_pimmediate ! () ; this prints the pstring immediately following it
|
||||||
asm {
|
asm {
|
||||||
.ptext "hello-pimmediate!{cr}"
|
.ptext "hello-pimmediate!{cr}"
|
||||||
}
|
}
|
||||||
|
|
||||||
A = 19
|
A = 19
|
||||||
fcall c64util.print_byte_decimal0
|
c64util.print_byte_decimal0 ! ()
|
||||||
A = 13
|
c64.CHROUT ! (13)
|
||||||
fcall c64.CHROUT
|
|
||||||
A = 19
|
A = 19
|
||||||
fcall c64util.print_byte_decimal
|
c64util.print_byte_decimal ! ()
|
||||||
A = 13
|
c64.CHROUT ! (13)
|
||||||
fcall c64.CHROUT
|
|
||||||
|
|
||||||
|
|
||||||
X = $01
|
X = $01
|
||||||
Y = $02
|
Y = $02
|
||||||
fcall c64util.print_word_decimal0
|
c64util.print_word_decimal0 ! ()
|
||||||
A = 13
|
c64.CHROUT ! (13)
|
||||||
fcall c64.CHROUT
|
|
||||||
X = $01
|
X = $01
|
||||||
Y = $02
|
Y = $02
|
||||||
fcall c64util.print_word_decimal
|
c64util.print_word_decimal ! ()
|
||||||
A = 13
|
c64.CHROUT ! (13)
|
||||||
fcall c64.CHROUT
|
|
||||||
return
|
return
|
||||||
|
|
||||||
start2
|
start2
|
||||||
call global2.make_screen_black
|
global2.make_screen_black()
|
||||||
call c64.CLEARSCR
|
c64.CLEARSCR()
|
||||||
XY = greeting
|
XY = greeting
|
||||||
call c64util.print_string
|
c64util.print_string()
|
||||||
XY = p_greeting
|
XY = p_greeting
|
||||||
call c64util.print_pstring
|
c64util.print_pstring()
|
||||||
A = 0
|
A = 0
|
||||||
call c64util.print_byte_decimal
|
c64util.print_byte_decimal()
|
||||||
A = 0
|
A = 0
|
||||||
call c64util.print_byte_hex
|
c64util.print_byte_hex()
|
||||||
|
c64.CHROUT(13)
|
||||||
|
c64util.print_byte_decimal()
|
||||||
A = 13
|
A = 13
|
||||||
call c64.CHROUT
|
c64util.print_byte_hex()
|
||||||
call c64util.print_byte_decimal
|
c64.CHROUT(13)
|
||||||
A = 13
|
|
||||||
call c64util.print_byte_hex
|
|
||||||
A = 13
|
|
||||||
call c64.CHROUT
|
|
||||||
A = 255
|
A = 255
|
||||||
call c64util.print_byte_decimal
|
c64util.print_byte_decimal()
|
||||||
A = 254
|
A = 254
|
||||||
call c64util.print_byte_hex
|
c64util.print_byte_hex()
|
||||||
A = 129
|
A = 129
|
||||||
call c64util.print_byte_hex
|
c64util.print_byte_hex()
|
||||||
A = 13
|
c64.CHROUT(13)
|
||||||
call c64.CHROUT
|
|
||||||
|
|
||||||
A = 13
|
c64.CHROUT(13)
|
||||||
call c64.CHROUT
|
|
||||||
X = 1
|
X = 1
|
||||||
Y = 0
|
Y = 0
|
||||||
call c64util.print_word_decimal
|
c64util.print_word_decimal()
|
||||||
A = 13
|
c64.CHROUT(13)
|
||||||
call c64.CHROUT
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user