mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
code generation
This commit is contained in:
parent
14e36a8708
commit
0bb5f98768
@ -126,7 +126,7 @@ class PlyParser:
|
|||||||
block.address = 0xc000
|
block.address = 0xc000
|
||||||
elif directive.args[0] == "prg":
|
elif directive.args[0] == "prg":
|
||||||
block.format = ProgramFormat.PRG
|
block.format = ProgramFormat.PRG
|
||||||
block.address = 0x0801
|
block.address = 0xc000
|
||||||
elif directive.args[0] == "basic":
|
elif directive.args[0] == "basic":
|
||||||
block.format = ProgramFormat.BASIC
|
block.format = ProgramFormat.BASIC
|
||||||
block.address = 0x0801
|
block.address = 0x0801
|
||||||
|
240
il65/datatypes.py
Normal file
240
il65/datatypes.py
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
"""
|
||||||
|
Programming Language for 6502/6510 microprocessors, codename 'Sick'
|
||||||
|
Here are the data type definitions and -conversions.
|
||||||
|
|
||||||
|
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import enum
|
||||||
|
from typing import Tuple, Union
|
||||||
|
from functools import total_ordering
|
||||||
|
from .plylex import print_warning, SourceRef
|
||||||
|
|
||||||
|
|
||||||
|
PrimitiveType = Union[int, float, str]
|
||||||
|
|
||||||
|
|
||||||
|
@total_ordering
|
||||||
|
class VarType(enum.Enum):
|
||||||
|
CONST = 1
|
||||||
|
MEMORY = 2
|
||||||
|
VAR = 3
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
if self.__class__ == other.__class__:
|
||||||
|
return self.value < other.value
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
@total_ordering
|
||||||
|
class DataType(enum.Enum):
|
||||||
|
"""The possible data types of values"""
|
||||||
|
BYTE = 1
|
||||||
|
WORD = 2
|
||||||
|
FLOAT = 3
|
||||||
|
BYTEARRAY = 4
|
||||||
|
WORDARRAY = 5
|
||||||
|
MATRIX = 6
|
||||||
|
STRING = 7
|
||||||
|
STRING_P = 8
|
||||||
|
STRING_S = 9
|
||||||
|
STRING_PS = 10
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
if self.__class__ == other.__class__:
|
||||||
|
return self.value < other.value
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS}
|
||||||
|
|
||||||
|
# 5-byte cbm MFLPT format limitations:
|
||||||
|
FLOAT_MAX_POSITIVE = 1.7014118345e+38
|
||||||
|
FLOAT_MAX_NEGATIVE = -1.7014118345e+38
|
||||||
|
|
||||||
|
|
||||||
|
def to_hex(number: int) -> str:
|
||||||
|
# 0..255 -> "$00".."$ff"
|
||||||
|
# 256..65536 -> "$0100".."$ffff"
|
||||||
|
if number is None:
|
||||||
|
raise ValueError("number")
|
||||||
|
if 0 <= number < 0x100:
|
||||||
|
return "${:02x}".format(number)
|
||||||
|
if 0 <= number < 0x10000:
|
||||||
|
return "${:04x}".format(number)
|
||||||
|
raise OverflowError(number)
|
||||||
|
|
||||||
|
|
||||||
|
def to_mflpt5(number: float) -> bytearray:
|
||||||
|
# algorithm here https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||||
|
number = float(number)
|
||||||
|
if number < FLOAT_MAX_NEGATIVE or number > FLOAT_MAX_POSITIVE:
|
||||||
|
raise OverflowError("floating point number out of 5-byte mflpt range", number)
|
||||||
|
if number == 0.0:
|
||||||
|
return bytearray([0, 0, 0, 0, 0])
|
||||||
|
if number < 0.0:
|
||||||
|
sign = 0x80000000
|
||||||
|
number = -number
|
||||||
|
else:
|
||||||
|
sign = 0x00000000
|
||||||
|
mant, exp = math.frexp(number)
|
||||||
|
exp += 128
|
||||||
|
if exp < 1:
|
||||||
|
# underflow, use zero instead
|
||||||
|
return bytearray([0, 0, 0, 0, 0])
|
||||||
|
if exp > 255:
|
||||||
|
raise OverflowError("floating point number out of 5-byte mflpt range", number)
|
||||||
|
mant = sign | int(mant * 0x100000000) & 0x7fffffff
|
||||||
|
return bytearray([exp]) + int.to_bytes(mant, 4, "big")
|
||||||
|
|
||||||
|
|
||||||
|
def mflpt5_to_float(mflpt: bytearray) -> float:
|
||||||
|
# algorithm here https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
|
||||||
|
if mflpt == bytearray([0, 0, 0, 0, 0]):
|
||||||
|
return 0.0
|
||||||
|
exp = mflpt[0] - 128
|
||||||
|
sign = mflpt[1] & 0x80
|
||||||
|
number = 0x80000000 | int.from_bytes(mflpt[1:], "big")
|
||||||
|
number = float(number) * 2**exp / 0x100000000
|
||||||
|
return -number if sign else number
|
||||||
|
|
||||||
|
|
||||||
|
def coerce_value(datatype: DataType, value: PrimitiveType, sourceref: SourceRef=None) -> Tuple[bool, PrimitiveType]:
|
||||||
|
# if we're a BYTE type, and the value is a single character, convert it to the numeric value
|
||||||
|
def verify_bounds(value: PrimitiveType) -> None:
|
||||||
|
# if the value is out of bounds, raise an overflow exception
|
||||||
|
if datatype == DataType.BYTE and not (0 <= value <= 0xff): # type: ignore
|
||||||
|
raise OverflowError("value out of range for byte")
|
||||||
|
if datatype == DataType.WORD and not (0 <= value <= 0xffff): # type: ignore
|
||||||
|
raise OverflowError("value out of range for word")
|
||||||
|
if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= value <= FLOAT_MAX_POSITIVE): # type: ignore
|
||||||
|
raise OverflowError("value out of range for float")
|
||||||
|
|
||||||
|
if datatype in (DataType.BYTE, DataType.BYTEARRAY, DataType.MATRIX) and isinstance(value, str):
|
||||||
|
if len(value) == 1:
|
||||||
|
return True, char_to_bytevalue(value)
|
||||||
|
# if we're an integer value and the passed value is float, truncate it (and give a warning)
|
||||||
|
if datatype in (DataType.BYTE, DataType.WORD, DataType.MATRIX) and isinstance(value, float):
|
||||||
|
frac = math.modf(value)
|
||||||
|
if frac != 0:
|
||||||
|
print_warning("float value truncated ({} to datatype {})".format(value, datatype.name), sourceref=sourceref)
|
||||||
|
value = int(value)
|
||||||
|
verify_bounds(value)
|
||||||
|
return True, value
|
||||||
|
verify_bounds(value)
|
||||||
|
return False, value
|
||||||
|
|
||||||
|
|
||||||
|
def char_to_bytevalue(character: str, petscii: bool=True) -> int:
|
||||||
|
assert len(character) == 1
|
||||||
|
if petscii:
|
||||||
|
return ord(character.translate(ascii_to_petscii_trans))
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("screencode conversion not yet implemented for chars")
|
||||||
|
|
||||||
|
|
||||||
|
# ASCII/UNICODE-to-PETSCII translation table
|
||||||
|
# Unicode symbols supported that map to a PETSCII character: £ ↑ ← ♠ ♥ ♦ ♣ π ● ○ and various others
|
||||||
|
ascii_to_petscii_trans = str.maketrans({
|
||||||
|
'\f': 147, # form feed becomes ClearScreen "{clear}"
|
||||||
|
'\n': 13, # line feed becomes a RETURN "{cr}" (not a line feed)
|
||||||
|
'\r': 17, # CR becomes CursorDown "{down}"
|
||||||
|
'a': 65,
|
||||||
|
'b': 66,
|
||||||
|
'c': 67,
|
||||||
|
'd': 68,
|
||||||
|
'e': 69,
|
||||||
|
'f': 70,
|
||||||
|
'g': 71,
|
||||||
|
'h': 72,
|
||||||
|
'i': 73,
|
||||||
|
'j': 74,
|
||||||
|
'k': 75,
|
||||||
|
'l': 76,
|
||||||
|
'm': 77,
|
||||||
|
'n': 78,
|
||||||
|
'o': 79,
|
||||||
|
'p': 80,
|
||||||
|
'q': 81,
|
||||||
|
'r': 82,
|
||||||
|
's': 83,
|
||||||
|
't': 84,
|
||||||
|
'u': 85,
|
||||||
|
'v': 86,
|
||||||
|
'w': 87,
|
||||||
|
'x': 88,
|
||||||
|
'y': 89,
|
||||||
|
'z': 90,
|
||||||
|
'A': 97,
|
||||||
|
'B': 98,
|
||||||
|
'C': 99,
|
||||||
|
'D': 100,
|
||||||
|
'E': 101,
|
||||||
|
'F': 102,
|
||||||
|
'G': 103,
|
||||||
|
'H': 104,
|
||||||
|
'I': 105,
|
||||||
|
'J': 106,
|
||||||
|
'K': 107,
|
||||||
|
'L': 108,
|
||||||
|
'M': 109,
|
||||||
|
'N': 110,
|
||||||
|
'O': 111,
|
||||||
|
'P': 112,
|
||||||
|
'Q': 113,
|
||||||
|
'R': 114,
|
||||||
|
'S': 115,
|
||||||
|
'T': 116,
|
||||||
|
'U': 117,
|
||||||
|
'V': 118,
|
||||||
|
'W': 119,
|
||||||
|
'X': 120,
|
||||||
|
'Y': 121,
|
||||||
|
'Z': 122,
|
||||||
|
'{': 179, # left squiggle
|
||||||
|
'}': 235, # right squiggle
|
||||||
|
'£': 92, # pound currency sign
|
||||||
|
'^': 94, # up arrow
|
||||||
|
'~': 126, # pi math symbol
|
||||||
|
'π': 126, # pi symbol
|
||||||
|
'`': 39, # single quote
|
||||||
|
'✓': 250, # check mark
|
||||||
|
|
||||||
|
'|': 221, # vertical bar
|
||||||
|
'│': 221, # vertical bar
|
||||||
|
'─': 96, # horizontal bar
|
||||||
|
'┼': 123, # vertical and horizontal bar
|
||||||
|
|
||||||
|
'↑': 94, # up arrow
|
||||||
|
'←': 95, # left arrow
|
||||||
|
|
||||||
|
'▔': 163, # upper bar
|
||||||
|
'_': 164, # lower bar (underscore)
|
||||||
|
'▁': 164, # lower bar
|
||||||
|
'▎': 165, # left bar
|
||||||
|
|
||||||
|
'♠': 97, # spades
|
||||||
|
'●': 113, # circle
|
||||||
|
'♥': 115, # hearts
|
||||||
|
'○': 119, # open circle
|
||||||
|
'♣': 120, # clubs
|
||||||
|
'♦': 122, # diamonds
|
||||||
|
|
||||||
|
'├': 171, # vertical and right
|
||||||
|
'┤': 179, # vertical and left
|
||||||
|
'┴': 177, # horiz and up
|
||||||
|
'┬': 178, # horiz and down
|
||||||
|
'└': 173, # up right
|
||||||
|
'┐': 174, # down left
|
||||||
|
'┌': 175, # down right
|
||||||
|
'┘': 189, # up left
|
||||||
|
'▗': 172, # block lr
|
||||||
|
'▖': 187, # block ll
|
||||||
|
'▝': 188, # block ur
|
||||||
|
'▘': 190, # block ul
|
||||||
|
'▚': 191, # block ul and lr
|
||||||
|
'▌': 161, # left half
|
||||||
|
'▄': 162, # lower half
|
||||||
|
'▒': 230, # raster
|
||||||
|
})
|
@ -5,13 +5,13 @@ This is the assembly code generator (from the parse tree)
|
|||||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import io
|
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import datetime
|
import datetime
|
||||||
from typing import Union
|
import itertools
|
||||||
from .plyparse import Module, ProgramFormat, Block, Directive, VarDef, Label, ZpOptions, DataType
|
from typing import Union, TextIO, List, Tuple, Iterator
|
||||||
from .symbols import to_hex
|
from .plyparse import Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions
|
||||||
|
from .datatypes import VarType, DataType, to_hex, mflpt5_to_float, to_mflpt5, STRING_DATATYPES
|
||||||
|
|
||||||
|
|
||||||
class CodeError(Exception):
|
class CodeError(Exception):
|
||||||
@ -24,17 +24,20 @@ class AssemblyGenerator:
|
|||||||
|
|
||||||
def __init__(self, module: Module) -> None:
|
def __init__(self, module: Module) -> None:
|
||||||
self.module = module
|
self.module = module
|
||||||
self.generated_code = io.StringIO()
|
self.cur_block = None
|
||||||
|
self.output = None # type: TextIO
|
||||||
|
|
||||||
def p(self, text, *args, **vargs):
|
def p(self, text, *args, **vargs):
|
||||||
# replace '\v' (vertical tab) char by the actual line indent (2 tabs) and write to the stringIo
|
# replace '\v' (vertical tab) char by the actual line indent (2 tabs) and write to the stringIo
|
||||||
print(text.replace("\v", "\t\t"), *args, file=self.generated_code, **vargs)
|
print(text.replace("\v", "\t\t"), *args, file=self.output, **vargs)
|
||||||
|
|
||||||
def generate(self, filename: str) -> None:
|
def generate(self, filename: str) -> None:
|
||||||
self._generate()
|
with open(filename, "wt") as self.output:
|
||||||
with open(filename, "wt") as out:
|
try:
|
||||||
out.write(self.generated_code.getvalue())
|
self._generate()
|
||||||
self.generated_code.close()
|
except Exception as x:
|
||||||
|
self.output.write(".error \"****** ABORTED DUE TO ERROR: " + str(x) + "\"\n")
|
||||||
|
raise
|
||||||
|
|
||||||
def _generate(self) -> None:
|
def _generate(self) -> None:
|
||||||
self.sanitycheck()
|
self.sanitycheck()
|
||||||
@ -93,7 +96,7 @@ class AssemblyGenerator:
|
|||||||
zpblock = self.module.zeropage()
|
zpblock = self.module.zeropage()
|
||||||
if zpblock:
|
if zpblock:
|
||||||
vars_to_init = [v for v in zpblock.scope.filter_nodes(VarDef)
|
vars_to_init = [v for v in zpblock.scope.filter_nodes(VarDef)
|
||||||
if v.allocate and v.type in (DataType.BYTE, DataType.WORD, DataType.FLOAT)]
|
if v.vartype == VarType.VAR and v.vartype in (DataType.BYTE, DataType.WORD, DataType.FLOAT)]
|
||||||
# @todo optimize sort order (sort on value first, then type, then blockname, then address/name)
|
# @todo optimize sort order (sort on value first, then type, then blockname, then address/name)
|
||||||
# (str(self.value) or "", self.blockname, self.name or "", self.address or 0, self.seq_nr)
|
# (str(self.value) or "", self.blockname, self.name or "", self.address or 0, self.seq_nr)
|
||||||
prev_value = 0 # type: Union[str, int, float]
|
prev_value = 0 # type: Union[str, int, float]
|
||||||
@ -131,6 +134,7 @@ class AssemblyGenerator:
|
|||||||
self.p("\v; there are no zp vars to initialize")
|
self.p("\v; there are no zp vars to initialize")
|
||||||
else:
|
else:
|
||||||
self.p("\v; there is no zp block to initialize")
|
self.p("\v; there is no zp block to initialize")
|
||||||
|
# @todo all block vars should be (re)initialized here as well!
|
||||||
if self.module.zp_options == ZpOptions.CLOBBER_RESTORE:
|
if self.module.zp_options == ZpOptions.CLOBBER_RESTORE:
|
||||||
self.p("\vjsr {:s}.start\v; call user code".format(self.module.main().label))
|
self.p("\vjsr {:s}.start\v; call user code".format(self.module.main().label))
|
||||||
self.p("\vcld")
|
self.p("\vcld")
|
||||||
@ -142,13 +146,190 @@ class AssemblyGenerator:
|
|||||||
self.p("_float_bytes_{:s}\v.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(varname, *bytes, fpvalue))
|
self.p("_float_bytes_{:s}\v.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(varname, *bytes, fpvalue))
|
||||||
self.p("\n")
|
self.p("\n")
|
||||||
|
|
||||||
def blocks(self):
|
def blocks(self) -> None:
|
||||||
self.p("; @todo") # @todo
|
zpblock = self.module.zeropage()
|
||||||
pass
|
if zpblock:
|
||||||
|
# if there's a Zeropage block, it always goes first
|
||||||
|
self.cur_block = zpblock # type: ignore
|
||||||
|
self.p("\n; ---- zero page block: '{:s}' ----\t\t; src l. {:d}\n".format(zpblock.sourceref.file, zpblock.sourceref.line))
|
||||||
|
self.p("{:s}\t.proc\n".format(zpblock.label))
|
||||||
|
self.generate_block_vars(zpblock)
|
||||||
|
self.p("\v.pend\n")
|
||||||
|
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
||||||
|
if block.name == "ZP":
|
||||||
|
continue # already processed
|
||||||
|
self.cur_block = block
|
||||||
|
self.p("\n; ---- next block: '{:s}' ----\t\t; src l. {:d}\n".format(block.sourceref.file, block.sourceref.line))
|
||||||
|
if block.address:
|
||||||
|
self.p(".cerror * > ${0:04x}, 'block address overlaps by ', *-${0:04x},' bytes'".format(block.address))
|
||||||
|
self.p("* = ${:04x}".format(block.address))
|
||||||
|
self.p("{:s}\t.proc\n".format(block.label))
|
||||||
|
self.generate_block_vars(block)
|
||||||
|
subroutines = list(sub for sub in block.scope.filter_nodes(Subroutine) if sub.address is not None)
|
||||||
|
if subroutines:
|
||||||
|
# these are (external) subroutines that are defined by address instead of a scope/code
|
||||||
|
self.p("; external subroutines")
|
||||||
|
for subdef in subroutines:
|
||||||
|
assert subdef.scope is None
|
||||||
|
self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address)))
|
||||||
|
self.p("; end external subroutines\n")
|
||||||
|
for stmt in block.scope.nodes:
|
||||||
|
if isinstance(stmt, Directive):
|
||||||
|
continue # should have been handled already
|
||||||
|
self.generate_statement(stmt)
|
||||||
|
if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start":
|
||||||
|
# make sure the main.start routine clears the decimal and carry flags as first steps
|
||||||
|
self.p("\vcld\t\t\t; clear decimal flag")
|
||||||
|
self.p("\vclc\t\t\t; clear carry flag")
|
||||||
|
self.p("\vclv\t\t\t; clear overflow flag")
|
||||||
|
subroutines = list(sub for sub in block.scope.filter_nodes(Subroutine) if sub.address is None)
|
||||||
|
if subroutines:
|
||||||
|
# these are subroutines that are defined by a scope/code
|
||||||
|
self.p("; -- block subroutines")
|
||||||
|
for subdef in subroutines:
|
||||||
|
assert subdef.scope is not None
|
||||||
|
self.p("{:s}\v; src l. {:d}".format(subdef.name, subdef.sourceref.line))
|
||||||
|
params = ", ".join("{:s} -> {:s}".format(name or "<unnamed>", registers) for name, registers in subdef.param_spec)
|
||||||
|
returns = ",".join(sorted(register for register in subdef.result_spec if register[-1] != '?'))
|
||||||
|
clobbers = ",".join(sorted(register for register in subdef.result_spec if register[-1] == '?'))
|
||||||
|
self.p("\v; params: {}\n\v; returns: {} clobbers: {}"
|
||||||
|
.format(params or "-", returns or "-", clobbers or "-"))
|
||||||
|
cur_block = self.cur_block
|
||||||
|
self.cur_block = subdef.scope
|
||||||
|
for stmt in subdef.scope.nodes:
|
||||||
|
if isinstance(stmt, Directive):
|
||||||
|
continue # should have been handled already
|
||||||
|
self.generate_statement(stmt)
|
||||||
|
self.cur_block = cur_block
|
||||||
|
self.p("")
|
||||||
|
self.p("; -- end block subroutines")
|
||||||
|
self.p("\n\v.pend\n")
|
||||||
|
|
||||||
def footer(self):
|
def footer(self) -> None:
|
||||||
self.p("; @todo") # @todo
|
self.p("\t.end")
|
||||||
pass
|
|
||||||
|
def output_string(self, value: str, screencodes: bool = False) -> str:
|
||||||
|
if len(value) == 1 and screencodes:
|
||||||
|
if value[0].isprintable() and ord(value[0]) < 128:
|
||||||
|
return "'{:s}'".format(value[0])
|
||||||
|
else:
|
||||||
|
return str(ord(value[0]))
|
||||||
|
result = '"'
|
||||||
|
for char in value:
|
||||||
|
if char in "{}":
|
||||||
|
result += '", {:d}, "'.format(ord(char))
|
||||||
|
elif char.isprintable() and ord(char) < 128:
|
||||||
|
result += char
|
||||||
|
else:
|
||||||
|
if screencodes:
|
||||||
|
result += '", {:d}, "'.format(ord(char))
|
||||||
|
else:
|
||||||
|
if char == '\f':
|
||||||
|
result += "{clear}"
|
||||||
|
elif char == '\b':
|
||||||
|
result += "{delete}"
|
||||||
|
elif char == '\n':
|
||||||
|
result += "{cr}"
|
||||||
|
elif char == '\r':
|
||||||
|
result += "{down}"
|
||||||
|
elif char == '\t':
|
||||||
|
result += "{tab}"
|
||||||
|
else:
|
||||||
|
result += '", {:d}, "'.format(ord(char))
|
||||||
|
return result + '"'
|
||||||
|
|
||||||
|
def generate_block_vars(self, block: Block) -> None:
|
||||||
|
# @todo block vars should be re-initialized when the program is run again, and not depend on statically prefilled data!
|
||||||
|
vars_by_vartype = itertools.groupby(block.scope.filter_nodes(VarDef), lambda c: c.vartype)
|
||||||
|
variable_definitions = sorted(vars_by_vartype, key=lambda gt: gt[0]) # type: List[Tuple[VarType, Iterator[VarDef]]]
|
||||||
|
for vartype, varnodes in variable_definitions:
|
||||||
|
if vartype == VarType.CONST:
|
||||||
|
self.p("; constants")
|
||||||
|
for vardef in varnodes:
|
||||||
|
if vardef.datatype == DataType.FLOAT:
|
||||||
|
self.p("\t\t{:s} = {}".format(vardef.name, vardef.value))
|
||||||
|
elif vardef.datatype in (DataType.BYTE, DataType.WORD):
|
||||||
|
self.p("\t\t{:s} = {:s}".format(vardef.name, to_hex(vardef.value)))
|
||||||
|
elif vardef.datatype in STRING_DATATYPES:
|
||||||
|
# a const string is just a string variable in the generated assembly
|
||||||
|
self._generate_string_var(vardef)
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid const type", vardef)
|
||||||
|
elif vartype == VarType.MEMORY:
|
||||||
|
self.p("; memory mapped variables")
|
||||||
|
for vardef in varnodes:
|
||||||
|
# create a definition for variables at a specific place in memory (memory-mapped)
|
||||||
|
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
assert vardef.size == [1]
|
||||||
|
self.p("\t\t{:s} = {:s}\t; {:s}".format(vardef.name, to_hex(vardef.value), vardef.datatype.name.lower()))
|
||||||
|
elif vardef.datatype == DataType.BYTEARRAY:
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
self.p("\t\t{:s} = {:s}\t; array of {:d} bytes".format(vardef.name, to_hex(vardef.value), vardef.size[0]))
|
||||||
|
elif vardef.datatype == DataType.WORDARRAY:
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
self.p("\t\t{:s} = {:s}\t; array of {:d} words".format(vardef.name, to_hex(vardef.value), vardef.size[0]))
|
||||||
|
elif vardef.datatype == DataType.MATRIX:
|
||||||
|
assert len(vardef.size) == 2
|
||||||
|
self.p("\t\t{:s} = {:s}\t; matrix {:d} by {:d} = {:d} bytes"
|
||||||
|
.format(vardef.name, to_hex(vardef.value), vardef.size[0], vardef.size[1], vardef.size[0]*vardef.size[1]))
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid var type")
|
||||||
|
elif vartype == VarType.VAR:
|
||||||
|
self.p("; normal variables")
|
||||||
|
for vardef in varnodes:
|
||||||
|
# create a definition for a variable that takes up space and will be initialized at startup
|
||||||
|
if vardef.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
assert vardef.size == [1]
|
||||||
|
if vardef.datatype == DataType.BYTE:
|
||||||
|
self.p("{:s}\t\t.byte {:s}".format(vardef.name, to_hex(int(vardef.value or 0))))
|
||||||
|
elif vardef.datatype == DataType.WORD:
|
||||||
|
self.p("{:s}\t\t.word {:s}".format(vardef.name, to_hex(int(vardef.value or 0))))
|
||||||
|
elif vardef.datatype == DataType.FLOAT:
|
||||||
|
self.p("{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}{:s}"
|
||||||
|
.format(vardef.name, *to_mflpt5(float(vardef.value or 0.0))))
|
||||||
|
else:
|
||||||
|
raise CodeError("weird datatype")
|
||||||
|
elif vardef.datatype in (DataType.BYTEARRAY, DataType.WORDARRAY):
|
||||||
|
assert len(vardef.size) == 1
|
||||||
|
if vardef.datatype == DataType.BYTEARRAY:
|
||||||
|
self.p("{:s}\t\t.fill {:d}, ${:02x}".format(vardef.name, vardef.size[0], vardef.value or 0))
|
||||||
|
elif vardef.datatype == 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}"
|
||||||
|
.format(vardef.name, vardef.size[0] * 2, f_lo, f_hi, vardef.size[0], vardef.value or 0))
|
||||||
|
else:
|
||||||
|
raise CodeError("invalid datatype", vardef.datatype)
|
||||||
|
elif vardef.datatype == DataType.MATRIX:
|
||||||
|
assert len(vardef.size) == 2
|
||||||
|
self.p("{:s}\t\t.fill {:d}, ${:02x}\t\t; matrix {:d}*{:d} bytes"
|
||||||
|
.format(vardef.name, vardef.size[0] * vardef.size[1], vardef.value or 0, vardef.size[0], vardef.size[1]))
|
||||||
|
elif vardef.datatype in STRING_DATATYPES:
|
||||||
|
self._generate_string_var(vardef)
|
||||||
|
else:
|
||||||
|
raise CodeError("unknown variable type " + str(vardef.datatype))
|
||||||
|
|
||||||
|
def _generate_string_var(self, vardef: VarDef) -> None:
|
||||||
|
if vardef.datatype == DataType.STRING:
|
||||||
|
# 0-terminated string
|
||||||
|
self.p("{:s}\n\v.null {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
||||||
|
elif vardef.datatype == DataType.STRING_P:
|
||||||
|
# pascal string
|
||||||
|
self.p("{:s}\n\v.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value))))
|
||||||
|
elif vardef.datatype == DataType.STRING_S:
|
||||||
|
# 0-terminated string in screencode encoding
|
||||||
|
self.p(".enc 'screen'")
|
||||||
|
self.p("{:s}\n\v.null {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
||||||
|
self.p(".enc 'none'")
|
||||||
|
elif vardef.datatype == DataType.STRING_PS:
|
||||||
|
# 0-terminated pascal string in screencode encoding
|
||||||
|
self.p(".enc 'screen'")
|
||||||
|
self.p("{:s}\n\v.ptext {:s}".format(vardef.name, self.output_string(str(vardef.value), True)))
|
||||||
|
self.p(".enc 'none'")
|
||||||
|
|
||||||
|
def generate_statement(self, stmt: AstNode) -> None:
|
||||||
|
if isinstance(stmt, Label):
|
||||||
|
self.p("\n{:s}\v\t\t; {:s}".format(stmt.name, stmt.lineref))
|
||||||
|
# @todo rest of the statement nodes
|
||||||
|
|
||||||
|
|
||||||
class Assembler64Tass:
|
class Assembler64Tass:
|
||||||
@ -164,7 +345,7 @@ class Assembler64Tass:
|
|||||||
elif self.format == ProgramFormat.RAW:
|
elif self.format == ProgramFormat.RAW:
|
||||||
args.append("--nostart")
|
args.append("--nostart")
|
||||||
else:
|
else:
|
||||||
raise ValueError("don't know how to create format "+str(self.format))
|
raise CodeError("don't know how to create format "+str(self.format))
|
||||||
try:
|
try:
|
||||||
if self.format == ProgramFormat.PRG:
|
if self.format == ProgramFormat.PRG:
|
||||||
print("\nCreating C-64 prg.")
|
print("\nCreating C-64 prg.")
|
||||||
|
@ -11,7 +11,7 @@ from typing import Union, Generator, Tuple, List, Optional, Dict
|
|||||||
import attr
|
import attr
|
||||||
from ply.yacc import yacc
|
from ply.yacc import yacc
|
||||||
from .plylex import SourceRef, tokens, lexer, find_tok_column
|
from .plylex import SourceRef, tokens, lexer, find_tok_column
|
||||||
from .symbols import DataType
|
from .datatypes import DataType, VarType, coerce_value
|
||||||
|
|
||||||
|
|
||||||
class ProgramFormat(enum.Enum):
|
class ProgramFormat(enum.Enum):
|
||||||
@ -71,7 +71,7 @@ class AstNode:
|
|||||||
|
|
||||||
def process_expressions(self) -> None:
|
def process_expressions(self) -> None:
|
||||||
# process/simplify all expressions (constant folding etc) @todo
|
# process/simplify all expressions (constant folding etc) @todo
|
||||||
# override in node types that have expression(s)
|
# @todo override in node types that have expression(s)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -319,21 +319,45 @@ class VarDef(AstNode):
|
|||||||
vartype = attr.ib()
|
vartype = attr.ib()
|
||||||
datatype = attr.ib()
|
datatype = attr.ib()
|
||||||
value = attr.ib(default=None)
|
value = attr.ib(default=None)
|
||||||
size = attr.ib(type=int, default=None)
|
size = attr.ib(type=list, default=None)
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
|
# convert vartype to enum
|
||||||
|
if self.vartype == "const":
|
||||||
|
self.vartype = VarType.CONST
|
||||||
|
elif self.vartype == "var":
|
||||||
|
self.vartype = VarType.VAR
|
||||||
|
elif self.vartype == "memory":
|
||||||
|
self.vartype = VarType.MEMORY
|
||||||
|
else:
|
||||||
|
raise ValueError("invalid vartype", self.vartype)
|
||||||
# convert datatype node to enum + size
|
# convert datatype node to enum + size
|
||||||
if self.datatype is None:
|
if self.datatype is None:
|
||||||
assert self.size is None
|
assert self.size is None
|
||||||
self.size = 1
|
self.size = [1]
|
||||||
self.datatype = DataType.BYTE
|
self.datatype = DataType.BYTE
|
||||||
elif isinstance(self.datatype, DatatypeNode):
|
elif isinstance(self.datatype, DatatypeNode):
|
||||||
assert self.size is None
|
assert self.size is None
|
||||||
self.size = self.datatype.dimensions
|
self.size = self.datatype.dimensions or [1]
|
||||||
self.datatype = self.datatype.to_enum()
|
self.datatype = self.datatype.to_enum()
|
||||||
# if the value is an expression, mark it as a *constant* expression here
|
# if the value is an expression, mark it as a *constant* expression here
|
||||||
if isinstance(self.value, Expression):
|
if isinstance(self.value, Expression):
|
||||||
self.value.processed_must_be_constant = True
|
self.value.processed_must_be_constant = True
|
||||||
|
elif self.value is None and self.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
|
self.value = 0
|
||||||
|
# note: value coercion is done later, when all expressions are evaluated
|
||||||
|
|
||||||
|
def process_expressions(self) -> None:
|
||||||
|
if isinstance(self.value, Expression):
|
||||||
|
# process/simplify all expressions (constant folding etc) # @todo
|
||||||
|
# verify that the expression yields a single constant value, replace value by that value # @todo
|
||||||
|
self.value = 123 # XXX
|
||||||
|
assert not isinstance(self.value, Expression)
|
||||||
|
if self.vartype in (VarType.CONST, VarType.VAR):
|
||||||
|
try:
|
||||||
|
_, self.value = coerce_value(self.datatype, self.value, self.sourceref)
|
||||||
|
except OverflowError as x:
|
||||||
|
raise ParseError(str(x), self.sourceref) from None
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, slots=True, repr=False)
|
@attr.s(cmp=False, slots=True, repr=False)
|
||||||
@ -359,8 +383,8 @@ class DatatypeNode(AstNode):
|
|||||||
@attr.s(cmp=False, repr=False)
|
@attr.s(cmp=False, repr=False)
|
||||||
class Subroutine(AstNode):
|
class Subroutine(AstNode):
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str)
|
||||||
param_spec = attr.ib()
|
param_spec = attr.ib(type=list)
|
||||||
result_spec = attr.ib()
|
result_spec = attr.ib(type=list)
|
||||||
scope = attr.ib(type=Scope, default=None)
|
scope = attr.ib(type=Scope, default=None)
|
||||||
address = attr.ib(type=int, default=None, validator=validate_address)
|
address = attr.ib(type=int, default=None, validator=validate_address)
|
||||||
|
|
||||||
@ -638,9 +662,9 @@ def p_subroutine(p):
|
|||||||
"""
|
"""
|
||||||
body = p[10]
|
body = p[10]
|
||||||
if isinstance(body, Scope):
|
if isinstance(body, Scope):
|
||||||
p[0] = Subroutine(name=p[2], param_spec=p[4], result_spec=p[8], scope=body, sourceref=_token_sref(p, 1))
|
p[0] = Subroutine(name=p[2], param_spec=p[4] or [], result_spec=p[8] or [], scope=body, sourceref=_token_sref(p, 1))
|
||||||
elif isinstance(body, int):
|
elif isinstance(body, int):
|
||||||
p[0] = Subroutine(name=p[2], param_spec=p[4], result_spec=p[8], address=body, sourceref=_token_sref(p, 1))
|
p[0] = Subroutine(name=p[2], param_spec=p[4] or [], result_spec=p[8] or [], address=body, sourceref=_token_sref(p, 1))
|
||||||
else:
|
else:
|
||||||
raise TypeError("subroutine_body", p.slice)
|
raise TypeError("subroutine_body", p.slice)
|
||||||
|
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
"""
|
|
||||||
Programming Language for 6502/6510 microprocessors, codename 'Sick'
|
|
||||||
Here are the symbol (name) operations such as lookups, datatype definitions.
|
|
||||||
|
|
||||||
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import enum
|
|
||||||
|
|
||||||
|
|
||||||
class DataType(enum.Enum):
|
|
||||||
"""The possible data types of values"""
|
|
||||||
BYTE = 1
|
|
||||||
WORD = 2
|
|
||||||
FLOAT = 3
|
|
||||||
BYTEARRAY = 4
|
|
||||||
WORDARRAY = 5
|
|
||||||
MATRIX = 6
|
|
||||||
STRING = 7
|
|
||||||
STRING_P = 8
|
|
||||||
STRING_S = 9
|
|
||||||
STRING_PS = 10
|
|
||||||
|
|
||||||
|
|
||||||
STRING_DATATYPES = {DataType.STRING, DataType.STRING_P, DataType.STRING_S, DataType.STRING_PS}
|
|
||||||
|
|
||||||
|
|
||||||
def to_hex(number: int) -> str:
|
|
||||||
# 0..255 -> "$00".."$ff"
|
|
||||||
# 256..65536 -> "$0100".."$ffff"
|
|
||||||
if number is None:
|
|
||||||
raise ValueError("number")
|
|
||||||
if 0 <= number < 0x100:
|
|
||||||
return "${:02x}".format(number)
|
|
||||||
if 0 <= number < 0x10000:
|
|
||||||
return "${:04x}".format(number)
|
|
||||||
raise OverflowError(number)
|
|
@ -1,11 +1,11 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from il65.symbols import DataType, STRING_DATATYPES, to_hex
|
from il65 import datatypes
|
||||||
from il65.compile import ParseError
|
from il65.compile import ParseError
|
||||||
from il65.plylex import SourceRef
|
from il65.plylex import SourceRef
|
||||||
|
|
||||||
|
|
||||||
def test_datatypes():
|
def test_datatypes():
|
||||||
assert all(isinstance(s, DataType) for s in STRING_DATATYPES)
|
assert all(isinstance(s, datatypes.DataType) for s in datatypes.STRING_DATATYPES)
|
||||||
|
|
||||||
|
|
||||||
def test_sourceref():
|
def test_sourceref():
|
||||||
@ -22,13 +22,93 @@ def test_parseerror():
|
|||||||
|
|
||||||
|
|
||||||
def test_to_hex():
|
def test_to_hex():
|
||||||
assert to_hex(0) == "$00"
|
assert datatypes.to_hex(0) == "$00"
|
||||||
assert to_hex(1) == "$01"
|
assert datatypes.to_hex(1) == "$01"
|
||||||
assert to_hex(255) == "$ff"
|
assert datatypes.to_hex(255) == "$ff"
|
||||||
assert to_hex(256) == "$0100"
|
assert datatypes.to_hex(256) == "$0100"
|
||||||
assert to_hex(20060) == "$4e5c"
|
assert datatypes.to_hex(20060) == "$4e5c"
|
||||||
assert to_hex(65535) == "$ffff"
|
assert datatypes.to_hex(65535) == "$ffff"
|
||||||
with pytest.raises(OverflowError):
|
with pytest.raises(OverflowError):
|
||||||
to_hex(-1)
|
datatypes.to_hex(-1)
|
||||||
with pytest.raises(OverflowError):
|
with pytest.raises(OverflowError):
|
||||||
to_hex(65536)
|
datatypes.to_hex(65536)
|
||||||
|
|
||||||
|
|
||||||
|
def test_float_to_mflpt5():
|
||||||
|
mflpt = datatypes.to_mflpt5(1.0)
|
||||||
|
assert type(mflpt) is bytearray
|
||||||
|
assert b"\x00\x00\x00\x00\x00" == datatypes.to_mflpt5(0)
|
||||||
|
assert b"\x82\x49\x0F\xDA\xA1" == datatypes.to_mflpt5(3.141592653)
|
||||||
|
assert b"\x82\x49\x0F\xDA\xA2" == datatypes.to_mflpt5(3.141592653589793)
|
||||||
|
assert b"\x90\x80\x00\x00\x00" == datatypes.to_mflpt5(-32768)
|
||||||
|
assert b"\x81\x00\x00\x00\x00" == datatypes.to_mflpt5(1)
|
||||||
|
assert b"\x80\x35\x04\xF3\x34" == datatypes.to_mflpt5(0.7071067812)
|
||||||
|
assert b"\x80\x35\x04\xF3\x33" == datatypes.to_mflpt5(0.7071067811865476)
|
||||||
|
assert b"\x81\x35\x04\xF3\x34" == datatypes.to_mflpt5(1.4142135624)
|
||||||
|
assert b"\x81\x35\x04\xF3\x33" == datatypes.to_mflpt5(1.4142135623730951)
|
||||||
|
assert b"\x80\x80\x00\x00\x00" == datatypes.to_mflpt5(-.5)
|
||||||
|
assert b"\x80\x31\x72\x17\xF8" == datatypes.to_mflpt5(0.69314718061)
|
||||||
|
assert b"\x80\x31\x72\x17\xF7" == datatypes.to_mflpt5(0.6931471805599453)
|
||||||
|
assert b"\x84\x20\x00\x00\x00" == datatypes.to_mflpt5(10)
|
||||||
|
assert b"\x9E\x6E\x6B\x28\x00" == datatypes.to_mflpt5(1000000000)
|
||||||
|
assert b"\x80\x00\x00\x00\x00" == datatypes.to_mflpt5(.5)
|
||||||
|
assert b"\x81\x38\xAA\x3B\x29" == datatypes.to_mflpt5(1.4426950408889634)
|
||||||
|
assert b"\x81\x49\x0F\xDA\xA2" == datatypes.to_mflpt5(1.5707963267948966)
|
||||||
|
assert b"\x83\x49\x0F\xDA\xA2" == datatypes.to_mflpt5(6.283185307179586)
|
||||||
|
assert b"\x7F\x00\x00\x00\x00" == datatypes.to_mflpt5(.25)
|
||||||
|
|
||||||
|
|
||||||
|
def test_float_range():
|
||||||
|
assert b"\xff\x7f\xff\xff\xff" == datatypes.to_mflpt5(datatypes.FLOAT_MAX_POSITIVE)
|
||||||
|
assert b"\xff\xff\xff\xff\xff" == datatypes.to_mflpt5(datatypes.FLOAT_MAX_NEGATIVE)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.to_mflpt5(1.7014118346e+38)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.to_mflpt5(-1.7014118346e+38)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.to_mflpt5(1.7014118347e+38)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.to_mflpt5(-1.7014118347e+38)
|
||||||
|
assert b"\x03\x39\x1d\x15\x63" == datatypes.to_mflpt5(1.7e-38)
|
||||||
|
assert b"\x00\x00\x00\x00\x00" == datatypes.to_mflpt5(1.7e-39)
|
||||||
|
assert b"\x03\xb9\x1d\x15\x63" == datatypes.to_mflpt5(-1.7e-38)
|
||||||
|
assert b"\x00\x00\x00\x00\x00" == datatypes.to_mflpt5(-1.7e-39)
|
||||||
|
|
||||||
|
|
||||||
|
def test_char_to_bytevalue():
|
||||||
|
assert datatypes.char_to_bytevalue('a') == 65
|
||||||
|
assert datatypes.char_to_bytevalue('\n') == 13
|
||||||
|
assert datatypes.char_to_bytevalue('π') == 126
|
||||||
|
assert datatypes.char_to_bytevalue('▒') == 230
|
||||||
|
assert datatypes.char_to_bytevalue('\x00') == 0
|
||||||
|
assert datatypes.char_to_bytevalue('\xff') == 255
|
||||||
|
with pytest.raises(AssertionError):
|
||||||
|
datatypes.char_to_bytevalue('<undefined>')
|
||||||
|
# screencodes not yet implemented: assert datatypes.char_to_bytevalue('a', False) == 65
|
||||||
|
|
||||||
|
|
||||||
|
def test_coerce_value():
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.BYTE, 0) == (False, 0)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.BYTE, 255) == (False, 255)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.WORD, 0) == (False, 0)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.WORD, 65535) == (False, 65535)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.FLOAT, -999.22) == (False, -999.22)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.FLOAT, 123.45) == (False, 123.45)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.BYTE, 5.678) == (True, 5)
|
||||||
|
assert datatypes.coerce_value(datatypes.DataType.WORD, 5.678) == (True, 5)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.BYTE, -1)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.BYTE, 256)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.BYTE, 256.12345)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.WORD, -1)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.WORD, 65536)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.WORD, 65536.12345)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.FLOAT, -1.7014118346e+38)
|
||||||
|
with pytest.raises(OverflowError):
|
||||||
|
datatypes.coerce_value(datatypes.DataType.FLOAT, 1.7014118347e+38)
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import pytest
|
|
||||||
from il65.handwritten import symbols, codegen
|
|
||||||
|
|
||||||
|
|
||||||
def test_float_to_mflpt5():
|
|
||||||
mflpt = codegen.CodeGenerator.to_mflpt5(1.0)
|
|
||||||
assert type(mflpt) is bytearray
|
|
||||||
assert b"\x00\x00\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(0)
|
|
||||||
assert b"\x82\x49\x0F\xDA\xA1" == codegen.CodeGenerator.to_mflpt5(3.141592653)
|
|
||||||
assert b"\x82\x49\x0F\xDA\xA2" == codegen.CodeGenerator.to_mflpt5(3.141592653589793)
|
|
||||||
assert b"\x90\x80\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(-32768)
|
|
||||||
assert b"\x81\x00\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(1)
|
|
||||||
assert b"\x80\x35\x04\xF3\x34" == codegen.CodeGenerator.to_mflpt5(0.7071067812)
|
|
||||||
assert b"\x80\x35\x04\xF3\x33" == codegen.CodeGenerator.to_mflpt5(0.7071067811865476)
|
|
||||||
assert b"\x81\x35\x04\xF3\x34" == codegen.CodeGenerator.to_mflpt5(1.4142135624)
|
|
||||||
assert b"\x81\x35\x04\xF3\x33" == codegen.CodeGenerator.to_mflpt5(1.4142135623730951)
|
|
||||||
assert b"\x80\x80\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(-.5)
|
|
||||||
assert b"\x80\x31\x72\x17\xF8" == codegen.CodeGenerator.to_mflpt5(0.69314718061)
|
|
||||||
assert b"\x80\x31\x72\x17\xF7" == codegen.CodeGenerator.to_mflpt5(0.6931471805599453)
|
|
||||||
assert b"\x84\x20\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(10)
|
|
||||||
assert b"\x9E\x6E\x6B\x28\x00" == codegen.CodeGenerator.to_mflpt5(1000000000)
|
|
||||||
assert b"\x80\x00\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(.5)
|
|
||||||
assert b"\x81\x38\xAA\x3B\x29" == codegen.CodeGenerator.to_mflpt5(1.4426950408889634)
|
|
||||||
assert b"\x81\x49\x0F\xDA\xA2" == codegen.CodeGenerator.to_mflpt5(1.5707963267948966)
|
|
||||||
assert b"\x83\x49\x0F\xDA\xA2" == codegen.CodeGenerator.to_mflpt5(6.283185307179586)
|
|
||||||
assert b"\x7F\x00\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(.25)
|
|
||||||
|
|
||||||
|
|
||||||
def test_float_range():
|
|
||||||
assert b"\xff\x7f\xff\xff\xff" == codegen.CodeGenerator.to_mflpt5(symbols.FLOAT_MAX_POSITIVE)
|
|
||||||
assert b"\xff\xff\xff\xff\xff" == codegen.CodeGenerator.to_mflpt5(symbols.FLOAT_MAX_NEGATIVE)
|
|
||||||
with pytest.raises(OverflowError):
|
|
||||||
codegen.CodeGenerator.to_mflpt5(1.7014118346e+38)
|
|
||||||
with pytest.raises(OverflowError):
|
|
||||||
codegen.CodeGenerator.to_mflpt5(-1.7014118346e+38)
|
|
||||||
with pytest.raises(OverflowError):
|
|
||||||
codegen.CodeGenerator.to_mflpt5(1.7014118347e+38)
|
|
||||||
with pytest.raises(OverflowError):
|
|
||||||
codegen.CodeGenerator.to_mflpt5(-1.7014118347e+38)
|
|
||||||
assert b"\x03\x39\x1d\x15\x63" == codegen.CodeGenerator.to_mflpt5(1.7e-38)
|
|
||||||
assert b"\x00\x00\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(1.7e-39)
|
|
||||||
assert b"\x03\xb9\x1d\x15\x63" == codegen.CodeGenerator.to_mflpt5(-1.7e-38)
|
|
||||||
assert b"\x00\x00\x00\x00\x00" == codegen.CodeGenerator.to_mflpt5(-1.7e-39)
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from il65.handwritten.symbols import Zeropage, SymbolError, DataType
|
from il65.handwritten.symbols import Zeropage, SymbolError, DataType # @todo
|
||||||
|
|
||||||
|
|
||||||
def test_zp_configure_onlyonce():
|
def test_zp_configure_onlyonce():
|
||||||
|
7
todo.ill
7
todo.ill
@ -1,19 +1,18 @@
|
|||||||
%output prg
|
%output basic
|
||||||
%saveregisters
|
%saveregisters
|
||||||
%import c64lib
|
%import c64lib
|
||||||
%import mathlib
|
%import mathlib
|
||||||
|
|
||||||
%address 22222
|
|
||||||
|
|
||||||
|
|
||||||
~ main $4444 {
|
~ main {
|
||||||
|
|
||||||
%saveregisters true
|
%saveregisters true
|
||||||
|
|
||||||
|
|
||||||
const num = 2
|
const num = 2
|
||||||
var var1 =2
|
var var1 =2
|
||||||
var .word wvar1 = 2 + foo() ; @todo constant
|
var .word wvar1 = 2 + foo() ; @todo constant check error
|
||||||
|
|
||||||
|
|
||||||
start:
|
start:
|
||||||
|
Loading…
Reference in New Issue
Block a user