mirror of
https://github.com/irmen/prog8.git
synced 2025-04-06 10:38:48 +00:00
float incr/decr
This commit is contained in:
parent
3dcfa42574
commit
6922ea2a0b
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
30
todo.ill
30
todo.ill
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user