float incr/decr

This commit is contained in:
Irmen de Jong 2018-01-01 05:04:04 +01:00
parent 3dcfa42574
commit 6922ea2a0b
6 changed files with 139 additions and 61 deletions

View File

@ -379,7 +379,7 @@ class AssignmentStmt(_AstNode):
else:
stringvar_name = "il65_str_{:d}".format(id(self))
value = self.right.value
containing_block.symbols.define_variable(stringvar_name, self.sourceref, DataType.STRING, value=value)
containing_block.symbols.define_constant(stringvar_name, self.sourceref, DataType.STRING, value=value)
self.right.name = stringvar_name
self._immediate_string_vars[self.right.value] = (containing_block.name, stringvar_name)
@ -419,19 +419,21 @@ class ReturnStmt(_AstNode):
class InplaceIncrStmt(_AstNode):
def __init__(self, what: Value, howmuch: Union[int, float], sourceref: SourceRef) -> None:
def __init__(self, what: Value, howmuch: Union[None, int, float], byname: Optional[str], sourceref: SourceRef) -> None:
super().__init__(sourceref)
assert howmuch > 0
assert howmuch is None or howmuch > 0
self.what = what
self.howmuch = howmuch
self.float_var_name = byname
class InplaceDecrStmt(_AstNode):
def __init__(self, what: Value, howmuch: Union[int, float], sourceref: SourceRef) -> None:
def __init__(self, what: Value, howmuch: Union[None, int, float], byname: Optional[str], sourceref: SourceRef) -> None:
super().__init__(sourceref)
assert howmuch > 0
assert howmuch is None or howmuch > 0
self.what = what
self.howmuch = howmuch
self.float_var_name = byname
class IfCondition(_AstNode):

View File

@ -296,25 +296,26 @@ class CodeGenerator:
self.p("; normal variables")
for vardef in non_mem_vars:
# create a definition for a variable that takes up space and will be initialized at startup
sourcecomment = "\t; " + vardef.sourcecomment if vardef.sourcecomment else ""
if vardef.type in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
if vardef.address:
assert block.name == "ZP", "only ZP-variables can be put on an address"
self.p("\t\t{:s} = {:s}".format(vardef.name, Parser.to_hex(vardef.address)))
else:
if vardef.type == DataType.BYTE:
self.p("{:s}\t\t.byte {:s}".format(vardef.name, Parser.to_hex(int(vardef.value))))
self.p("{:s}\t\t.byte {:s}{:s}".format(vardef.name, Parser.to_hex(int(vardef.value)), sourcecomment))
elif vardef.type == DataType.WORD:
self.p("{:s}\t\t.word {:s}".format(vardef.name, Parser.to_hex(int(vardef.value))))
self.p("{:s}\t\t.word {:s}{:s}".format(vardef.name, Parser.to_hex(int(vardef.value)), sourcecomment))
elif vardef.type == DataType.FLOAT:
self.p("{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}"
.format(vardef.name, *self.to_mflpt5(float(vardef.value))))
self.p("{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}{:s}"
.format(vardef.name, *self.to_mflpt5(float(vardef.value)), sourcecomment))
else:
raise CodeError("weird datatype")
elif vardef.type in (DataType.BYTEARRAY, DataType.WORDARRAY):
if vardef.address:
raise CodeError("array or wordarray vars must not have address; will be allocated by assembler")
if vardef.type == DataType.BYTEARRAY:
self.p("{:s}\t\t.fill {:d}, ${:02x}".format(vardef.name, vardef.length, vardef.value or 0))
self.p("{:s}\t\t.fill {:d}, ${:02x}{:s}".format(vardef.name, vardef.length, vardef.value or 0, sourcecomment))
elif vardef.type == DataType.WORDARRAY:
f_hi, f_lo = divmod(vardef.value or 0, 256) # type: ignore
self.p("{:s}\t\t.fill {:d}, [${:02x}, ${:02x}]\t; {:d} words of ${:04x}"
@ -395,7 +396,7 @@ class CodeGenerator:
self.previous_stmt_was_assignment = isinstance(stmt, AssignmentStmt)
def generate_incr_or_decr(self, stmt: Union[InplaceIncrStmt, InplaceDecrStmt]) -> None:
assert stmt.howmuch > 0
assert (stmt.howmuch is None and stmt.float_var_name) or (stmt.howmuch > 0 and not stmt.float_var_name)
if stmt.what.datatype != DataType.FLOAT and stmt.howmuch > 0xff:
raise CodeError("only supports integer incr/decr by up to 255 for now") # XXX
is_incr = isinstance(stmt, InplaceIncrStmt)
@ -562,16 +563,28 @@ class CodeGenerator:
self.p("+\t\tpla")
elif what.datatype == DataType.FLOAT:
if stmt.howmuch == 1.0:
t_str = stmt.what.name or Parser.to_hex(stmt.what.address)
# special case for +/-1
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
self.p("\t\t ldx #<" + t_str)
self.p("\t\t ldy #>" + t_str)
self.p("\t\tldx #<" + r_str)
self.p("\t\tldy #>" + r_str)
if is_incr:
self.p("\t\t jsr il65_lib.float_add_one")
self.p("\t\tjsr il65_lib.float_add_one")
else:
self.p("\t\t jsr il65_lib.float_sub_one")
self.p("\t\tjsr il65_lib.float_sub_one")
elif stmt.float_var_name:
with self.preserving_registers({'A', 'X', 'Y'}, loads_a_within=True):
self.p("\t\tlda #<" + stmt.float_var_name)
self.p("\t\tsta c64.SCRATCH_ZPWORD1")
self.p("\t\tlda #>" + stmt.float_var_name)
self.p("\t\tsta c64.SCRATCH_ZPWORD1+1")
self.p("\t\tldx #<" + r_str)
self.p("\t\tldy #>" + r_str)
if is_incr:
self.p("\t\tjsr il65_lib.float_add_SW1_to_XY")
else:
self.p("\t\tjsr il65_lib.float_sub_SW1_from_XY")
else:
raise CodeError("cannot incr/decr float by other than 1 at this time", stmt.howmuch) # XXX
raise CodeError("incr/decr missing float constant definition")
else:
raise CodeError("cannot in/decrement memory of type " + str(what.datatype), stmt.howmuch)
else:
@ -1015,7 +1028,7 @@ class CodeGenerator:
else:
raise CodeError("invalid rvalue for augmented assignment on register", str(rvalue))
else:
raise CodeError("augmented assignment only implemented for registers for now") # XXX
raise CodeError("augmented assignment only implemented for registers for now", str(rvalue)) # XXX
def _generate_aug_reg_mem(self, lvalue: RegisterValue, operator: str, rvalue: MemMappedValue) -> None:
r_str = rvalue.name or Parser.to_hex(rvalue.address)

View File

@ -225,6 +225,8 @@ class Parser:
break
self.print_warning("{:s} doesn't end with a return statement".format(message), block.sourceref)
_immediate_floats = {} # type: Dict[float, Tuple[str, str]]
def _parse_2(self) -> None:
# parsing pass 2 (not done during preprocessing!)
self.cur_block = None
@ -243,6 +245,49 @@ class Parser:
self.sourceref = stmt.sourceref.copy()
stmt.desugar_immediate_string(containing_block)
def desugar_immediate_floats(stmt: _AstNode, containing_block: Block) -> None:
if isinstance(stmt, (InplaceIncrStmt, InplaceDecrStmt)):
if stmt.howmuch is None:
assert stmt.float_var_name
return
if stmt.howmuch in (0, 1):
return # 1 is special cased in the code generator
rom_floats = {
1: "c64.FL_FONE",
.25: "c64.FL_FR4",
.5: "c64.FL_FHALF",
-.5: "c64.FL_NEGHLF",
10: "c64.FL_TENC",
-32768: "c64.FL_N32768",
1e9: "c64.FL_NZMIL",
math.pi: "c64.FL_PIVAL",
math.pi / 2: "c64.FL_PIHALF",
math.pi * 2: "c64.FL_TWOPI",
math.sqrt(2)/2.0: "c64.FL_SQRHLF",
math.sqrt(2): "c64.FL_SQRTWO",
math.log(2): "c64.FL_LOG2",
1.0 / math.log(2): "c64.FL_LOGEB2",
}
for fv, name in rom_floats.items():
if math.isclose(stmt.howmuch, fv, rel_tol=0, abs_tol=1e-9):
# use one of the constants available in ROM
stmt.float_var_name = name
return
if stmt.howmuch in self._immediate_floats:
# reuse previously defined float constant
blockname, floatvar_name = self._immediate_floats[stmt.howmuch]
if blockname:
stmt.float_var_name = blockname + '.' + floatvar_name
else:
stmt.float_var_name = floatvar_name
else:
# define new float variable to hold the incr/decr value
# note: not a constant, because we need the MFLT bytes
floatvar_name = "il65_float_{:d}".format(id(stmt))
containing_block.symbols.define_variable(floatvar_name, stmt.sourceref, DataType.FLOAT, value=stmt.howmuch)
self._immediate_floats[stmt.howmuch] = (containing_block.name, floatvar_name)
stmt.float_var_name = floatvar_name
for block in self.result.blocks:
self.cur_block = block
self.sourceref = block.sourceref.copy()
@ -252,6 +297,7 @@ class Parser:
self.sourceref = stmt.sourceref.copy()
self.desugar_call_arguments_and_outputs(stmt)
desugar_immediate_strings(stmt, self.cur_block)
desugar_immediate_floats(stmt, self.cur_block)
def desugar_call_arguments_and_outputs(self, stmt: CallStmt) -> None:
stmt.desugared_call_arguments.clear()
@ -764,8 +810,8 @@ class Parser:
if isinstance(what, IntegerValue):
raise self.PError("cannot in/decrement a constant value")
if incr:
return InplaceIncrStmt(what, 1, self.sourceref)
return InplaceDecrStmt(what, 1, self.sourceref)
return InplaceIncrStmt(what, 1, None, self.sourceref)
return InplaceDecrStmt(what, 1, None, self.sourceref)
else:
# perhaps it is an augmented assignment statement
match = re.fullmatch(r"(?P<left>\S+)\s*(?P<assignment>\+=|-=|\*=|/=|%=|//=|\*\*=|&=|\|=|\^=|>>=|<<=)\s*(?P<right>\S.*)", line)
@ -903,21 +949,28 @@ class Parser:
truncated, value = self.coerce_value(self.sourceref, l_value.datatype, r_value.value)
if truncated:
r_value = IntegerValue(int(value), self.sourceref, datatype=l_value.datatype, name=r_value.name)
if r_value.constant and operator in ("+=", "-="):
if operator == "+=":
if r_value.value > 0: # type: ignore
return InplaceIncrStmt(l_value, r_value.value, self.sourceref) # type: ignore
elif r_value.value < 0: # type: ignore
return InplaceDecrStmt(l_value, -r_value.value, self.sourceref) # type: ignore
if operator in ("+=", "-="):
# see if we can simplify this to inplace incr/decr statement
if r_value.constant:
if operator == "+=":
if r_value.value > 0: # type: ignore
return InplaceIncrStmt(l_value, r_value.value, self.sourceref) # type: ignore
elif r_value.value < 0: # type: ignore
return InplaceDecrStmt(l_value, -r_value.value, self.sourceref) # type: ignore
else:
self.print_warning("incr with zero, ignored")
else:
self.print_warning("incr with zero, ignored")
if r_value.value > 0: # type: ignore
return InplaceDecrStmt(l_value, r_value.value, self.sourceref) # type: ignore
elif r_value.value < 0: # type: ignore
return InplaceIncrStmt(l_value, -r_value.value, self.sourceref) # type: ignore
else:
self.print_warning("decr with zero, ignored")
else:
if r_value.value > 0: # type: ignore
return InplaceDecrStmt(l_value, r_value.value, self.sourceref) # type: ignore
elif r_value.value < 0: # type: ignore
return InplaceIncrStmt(l_value, -r_value.value, self.sourceref) # type: ignore
else:
self.print_warning("decr with zero, ignored")
if r_value.name:
if operator == "+=":
return InplaceIncrStmt(l_value, None, r_value.name, self.sourceref)
return InplaceDecrStmt(l_value, None, r_value.name, self.sourceref)
return AugmentedAssignmentStmt(l_value, operator, r_value, self.sourceref)
def parse_return(self, line: str) -> ReturnStmt:
@ -1022,7 +1075,7 @@ class Parser:
elif isinstance(expression, MemMappedValue):
return IntegerValue(expression.address, self.sourceref, datatype=DataType.WORD, name=expression.name)
else:
raise self.PError("cannot take the address of this type")
raise self.PError("cannot take the address of type " + expression.__class__.__name__)
elif text[0] in "-.0123456789$%~":
number = parse_expr_as_number(text, self.cur_block.symbols, self.ppsymbols, self.sourceref)
try:

View File

@ -121,7 +121,7 @@ class VariableDef(SymbolDefinition):
def __init__(self, blockname: str, name: str, sourceref: SourceRef,
datatype: DataType, allocate: bool, *,
value: PrimitiveType, length: int, address: Optional[int]=None,
register: str=None, matrixsize: Tuple[int, int]=None) -> None:
register: str=None, matrixsize: Tuple[int, int]=None, sourcecomment: str="") -> None:
super().__init__(blockname, name, sourceref, allocate)
self.type = datatype
self.address = address
@ -129,6 +129,7 @@ class VariableDef(SymbolDefinition):
self.value = value
self.register = register
self.matrixsize = matrixsize
self.sourcecomment = sourcecomment
@property
def is_memmap(self):
@ -406,8 +407,9 @@ class SymbolTable:
address = self._zeropage.allocate(name, datatype)
except LookupError:
raise SymbolError("no space in ZP left for global 5-byte MFLT float variable (try zp clobber)")
sourcecomment = "float " + str(value)
self.symbols[name] = VariableDef(self.name, name, sourceref, DataType.FLOAT, allocate,
value=value, length=1, address=address)
value=value, length=1, address=address, sourcecomment=sourcecomment)
elif datatype == DataType.BYTEARRAY:
self.symbols[name] = VariableDef(self.name, name, sourceref, DataType.BYTEARRAY, allocate,
value=value, length=length, address=address)

View File

@ -151,5 +151,33 @@ float_sub_one
ldy SCRATCH_ZP2
jmp c64.FTOMEMXY ; float XY = fac1
; ---- add MFLT pointed to by SCRATCH_ZPWORD1 to the MFLT pointed to by X/Y. Clobbers A, X, Y
float_add_SW1_to_XY
stx SCRATCH_ZP1
sty SCRATCH_ZP2
txa
jsr c64.MOVFM ; fac1 = float XY
lda SCRATCH_ZPWORD1
ldy SCRATCH_ZPWORD1+1
jsr c64.FADD ; fac1 += SCRATCH_ZPWORD1
ldx SCRATCH_ZP1
ldy SCRATCH_ZP2
jmp c64.FTOMEMXY ; float XY = fac1
; ---- subtract MFLT pointed to by SCRATCH_ZPWORD1 from the MFLT pointed to by X/Y. Clobbers A, X, Y
float_sub_SW1_from_XY
stx SCRATCH_ZP1
sty SCRATCH_ZP2
lda SCRATCH_ZPWORD1
ldy SCRATCH_ZPWORD1+1
jsr c64.MOVFM ; fac1 = SCRATCH_ZPWORD1
txa
ldy SCRATCH_ZP2
jsr c64.FSUB ; fac1 = float XY - SCRATCH_ZPWORD1
ldx SCRATCH_ZP1
ldy SCRATCH_ZP2
jmp c64.FTOMEMXY ; float XY = fac1
}
}

View File

@ -7,32 +7,12 @@ import "c64lib"
~ main {
var .float float1 = 123.456
start
c64.MOVFM(#float1)
c64.FPRINTLN()
float1++
c64.MOVFM(#float1)
c64.FPRINTLN()
float1-=1
c64.MOVFM(#float1)
c64.FPRINTLN()
float1--
c64.MOVFM(#float1)
c64.FPRINTLN()
;float1+=0.5
;c64.MOVFM(#float1)
;c64.FPRINTLN()
;float1+=2235.55
;c64.MOVFM(#float1)
;c64.FPRINTLN()
;float1-=999.55
;c64.MOVFM(#float1)
;c64.FPRINTLN()
;float1-=33333.456
;c64.MOVFM(#float1)
;c64.FPRINTLN()
var .float float2 = 99.99
const .float flc = 1234.55
const .text foostr = "derp"
var .text foostr2 = "derp2"
return
start
A = $11
X = $22