restructure codegen

This commit is contained in:
Irmen de Jong 2018-03-06 22:25:45 +01:00
parent 44c0d243ef
commit 4d929e00f5
16 changed files with 171 additions and 152 deletions

1
il65/codegen/__init__.py Normal file
View File

@ -0,0 +1 @@
# package

View File

@ -1,21 +1,15 @@
"""
Programming Language for 6502/6510 microprocessors, codename 'Sick'
This is the assembly code generator (from the parse tree)
This is the 6502 assembly code generator (directly from the parse tree)
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
import contextlib
import math
import attr
from typing import Set, Callable, no_type_check
from ..datatypes import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE
from ..plyparse import Scope, AstNode
from ..compile import Zeropage
class CodeError(Exception):
pass
from typing import Set, Callable
from ...plyparse import Scope, AstNode
from ...compile import Zeropage
@attr.s(repr=False, cmp=False)
@ -26,56 +20,6 @@ class Context:
floats_enabled = attr.ib(type=bool)
def to_hex(number: int) -> str:
# 0..15 -> "0".."15"
# 16..255 -> "$10".."$ff"
# 256..65536 -> "$0100".."$ffff"
assert type(number) is int
if number is None:
raise ValueError("number")
if 0 <= number < 16:
return str(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
@contextlib.contextmanager
def preserving_registers(registers: Set[str], scope: Scope, out: Callable, loads_a_within: bool=False, force_preserve: bool=False):
# this sometimes clobbers a ZP scratch register and is therefore NOT safe to use in interrupts

View File

@ -6,10 +6,11 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
from typing import Callable
from ..plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef, Dereference
from . import CodeError, preserving_registers, to_hex, Context
from ..datatypes import REGISTER_BYTES, VarType, DataType
from ..compile import Zeropage
from . import preserving_registers, Context
from ..shared import CodeError, to_hex
from ...plyparse import Scope, Assignment, AugAssignment, Register, LiteralValue, SymbolName, VarDef, Dereference
from ...datatypes import REGISTER_BYTES, VarType, DataType
from ...compile import Zeropage
def generate_assignment(ctx: Context) -> None:

View File

@ -5,8 +5,9 @@ This is the code generator for gotos and subroutine calls.
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
from ..plyparse import Goto, SubCall, LiteralValue, SymbolName, Dereference, Register
from . import Context, CodeError, to_hex
from . import Context
from ..shared import CodeError, to_hex
from ...plyparse import Goto, SubCall, LiteralValue, SymbolName, Dereference
def generate_goto(ctx: Context) -> None:

View File

@ -8,14 +8,15 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
import os
import datetime
from typing import TextIO, Callable, no_type_check
from ..plylex import print_bold
from ..plyparse import (Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, ZpOptions,
InlineAssembly, Return, Register, Goto, SubCall, Assignment, AugAssignment, IncrDecr)
from . import CodeError, to_hex, to_mflpt5, Context
from . import Context
from .variables import generate_block_init, generate_block_vars
from .assignment import generate_assignment, generate_aug_assignment
from .calls import generate_goto, generate_subcall
from .incrdecr import generate_incrdecr
from ..shared import CodeError, to_hex, to_mflpt5
from ...plylex import print_bold
from ...plyparse import (Module, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, ZpOptions,
InlineAssembly, Return, Register, Goto, SubCall, Assignment, AugAssignment, IncrDecr)
class Output:

View File

@ -7,9 +7,10 @@ is quite frequent and this generates assembly code tweaked for this case.
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
from ..plyparse import VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue, scoped_name
from ..datatypes import VarType, DataType, REGISTER_BYTES
from . import CodeError, preserving_registers, to_hex, Context
from . import Context, preserving_registers
from ..shared import CodeError, to_hex
from ...plyparse import VarDef, Register, IncrDecr, SymbolName, Dereference, LiteralValue, scoped_name
from ...datatypes import VarType, DataType, REGISTER_BYTES
def generate_incrdecr(ctx: Context) -> None:

View File

@ -7,9 +7,9 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
from collections import defaultdict
from typing import Dict, List, Callable, Any, no_type_check
from ..plyparse import Block, VarDef, LiteralValue, AddressOf
from ..datatypes import DataType, VarType, STRING_DATATYPES
from . import to_hex, to_mflpt5, CodeError
from ..shared import to_hex, to_mflpt5, CodeError
from ...plyparse import Block, VarDef, LiteralValue, AddressOf
from ...datatypes import DataType, VarType, STRING_DATATYPES
def generate_block_init(out: Callable, block: Block) -> None:

64
il65/codegen/shared.py Normal file
View File

@ -0,0 +1,64 @@
"""
Programming Language for 6502/6510 microprocessors, codename 'Sick'
Shared logic for the code generators.
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
import math
from ..datatypes import FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE
class CodeError(Exception):
pass
def to_hex(number: int) -> str:
# 0..15 -> "0".."15"
# 16..255 -> "$10".."$ff"
# 256..65536 -> "$0100".."$ffff"
assert type(number) is int
if number is None:
raise ValueError("number")
if 0 <= number < 16:
return str(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

View File

@ -0,0 +1,8 @@
"""
Programming Language for 6502/6510 microprocessors, codename 'Sick'
This is the tinyvm stack based program generator
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
# @todo

View File

@ -12,7 +12,7 @@ import argparse
import subprocess
from .compile import PlyParser
from .optimize import optimize
from .emit.generate import AssemblyGenerator
from .codegen.mos6502.generate import AssemblyGenerator
from .plylex import print_bold
from .plyparse import ProgramFormat

View File

@ -18,12 +18,13 @@ from .datatypes import (DataType, VarType, REGISTER_SYMBOLS, REGISTER_BYTES, REG
FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE, char_to_bytevalue)
__all__ = ["ProgramFormat", "ZpOptions", "math_functions", "builtin_functions", "ParseError", "ExpressionEvaluationError",
"UndefinedSymbolError", "AstNode", "Directive", "Scope", "Block", "Module", "Label", "Expression",
__all__ = ["ProgramFormat", "ZpOptions", "math_functions", "builtin_functions",
"AstNode", "Directive", "Scope", "Block", "Module", "Label", "Expression",
"Register", "Subroutine", "LiteralValue", "AddressOf", "SymbolName", "Dereference", "IncrDecr",
"ExpressionWithOperator", "Goto", "SubCall", "VarDef", "Return", "Assignment", "AugAssignment",
"InlineAssembly", "BuiltinFunction", "TokenFilter", "parser", "connect_parents", "DatatypeNode",
"parse_file", "coerce_constant_value", "datatype_of", "check_symbol_definition", "NotCompiletimeConstantError"]
"parse_file", "coerce_constant_value", "datatype_of", "check_symbol_definition", "scoped_name",
"NotCompiletimeConstantError", "ExpressionEvaluationError", "ParseError", "UndefinedSymbolError"]
class ProgramFormat(enum.Enum):

View File

@ -0,0 +1 @@
# package

View File

@ -0,0 +1,60 @@
import pytest
from il65.datatypes import FLOAT_MAX_NEGATIVE, FLOAT_MAX_POSITIVE
from il65.codegen.shared import to_hex, to_mflpt5
def test_to_hex():
assert to_hex(0) == "0"
assert to_hex(1) == "1"
assert to_hex(10) == "10"
assert to_hex(15) == "15"
assert to_hex(16) == "$10"
assert to_hex(255) == "$ff"
assert to_hex(256) == "$0100"
assert to_hex(20060) == "$4e5c"
assert to_hex(65535) == "$ffff"
with pytest.raises(OverflowError):
to_hex(-1)
with pytest.raises(OverflowError):
to_hex(65536)
def test_float_to_mflpt5():
mflpt = to_mflpt5(1.0)
assert type(mflpt) is bytearray
assert b"\x00\x00\x00\x00\x00" == to_mflpt5(0)
assert b"\x82\x49\x0F\xDA\xA1" == to_mflpt5(3.141592653)
assert b"\x82\x49\x0F\xDA\xA2" == to_mflpt5(3.141592653589793)
assert b"\x90\x80\x00\x00\x00" == to_mflpt5(-32768)
assert b"\x81\x00\x00\x00\x00" == to_mflpt5(1)
assert b"\x80\x35\x04\xF3\x34" == to_mflpt5(0.7071067812)
assert b"\x80\x35\x04\xF3\x33" == to_mflpt5(0.7071067811865476)
assert b"\x81\x35\x04\xF3\x34" == to_mflpt5(1.4142135624)
assert b"\x81\x35\x04\xF3\x33" == to_mflpt5(1.4142135623730951)
assert b"\x80\x80\x00\x00\x00" == to_mflpt5(-.5)
assert b"\x80\x31\x72\x17\xF8" == to_mflpt5(0.69314718061)
assert b"\x80\x31\x72\x17\xF7" == to_mflpt5(0.6931471805599453)
assert b"\x84\x20\x00\x00\x00" == to_mflpt5(10)
assert b"\x9E\x6E\x6B\x28\x00" == to_mflpt5(1000000000)
assert b"\x80\x00\x00\x00\x00" == to_mflpt5(.5)
assert b"\x81\x38\xAA\x3B\x29" == to_mflpt5(1.4426950408889634)
assert b"\x81\x49\x0F\xDA\xA2" == to_mflpt5(1.5707963267948966)
assert b"\x83\x49\x0F\xDA\xA2" == to_mflpt5(6.283185307179586)
assert b"\x7F\x00\x00\x00\x00" == to_mflpt5(.25)
def test_float_range():
assert b"\xff\x7f\xff\xff\xff" == to_mflpt5(FLOAT_MAX_POSITIVE)
assert b"\xff\xff\xff\xff\xff" == to_mflpt5(FLOAT_MAX_NEGATIVE)
with pytest.raises(OverflowError):
to_mflpt5(1.7014118346e+38)
with pytest.raises(OverflowError):
to_mflpt5(-1.7014118346e+38)
with pytest.raises(OverflowError):
to_mflpt5(1.7014118347e+38)
with pytest.raises(OverflowError):
to_mflpt5(-1.7014118347e+38)
assert b"\x03\x39\x1d\x15\x63" == to_mflpt5(1.7e-38)
assert b"\x00\x00\x00\x00\x00" == to_mflpt5(1.7e-39)
assert b"\x03\xb9\x1d\x15\x63" == to_mflpt5(-1.7e-38)
assert b"\x00\x00\x00\x00\x00" == to_mflpt5(-1.7e-39)

View File

@ -1,9 +1,8 @@
import pytest
from il65.datatypes import DataType, STRING_DATATYPES, FLOAT_MAX_POSITIVE, FLOAT_MAX_NEGATIVE, char_to_bytevalue
from il65.datatypes import DataType, STRING_DATATYPES, char_to_bytevalue
from il65.plyparse import coerce_constant_value, LiteralValue, Scope, SymbolName, VarDef
from il65.compile import ParseError
from il65.plylex import SourceRef
from il65.emit import to_hex, to_mflpt5
def test_datatypes():
@ -31,63 +30,6 @@ def test_parseerror():
assert str(p) == "filename:99:42 message"
def test_to_hex():
assert to_hex(0) == "0"
assert to_hex(1) == "1"
assert to_hex(10) == "10"
assert to_hex(15) == "15"
assert to_hex(16) == "$10"
assert to_hex(255) == "$ff"
assert to_hex(256) == "$0100"
assert to_hex(20060) == "$4e5c"
assert to_hex(65535) == "$ffff"
with pytest.raises(OverflowError):
to_hex(-1)
with pytest.raises(OverflowError):
to_hex(65536)
def test_float_to_mflpt5():
mflpt = to_mflpt5(1.0)
assert type(mflpt) is bytearray
assert b"\x00\x00\x00\x00\x00" == to_mflpt5(0)
assert b"\x82\x49\x0F\xDA\xA1" == to_mflpt5(3.141592653)
assert b"\x82\x49\x0F\xDA\xA2" == to_mflpt5(3.141592653589793)
assert b"\x90\x80\x00\x00\x00" == to_mflpt5(-32768)
assert b"\x81\x00\x00\x00\x00" == to_mflpt5(1)
assert b"\x80\x35\x04\xF3\x34" == to_mflpt5(0.7071067812)
assert b"\x80\x35\x04\xF3\x33" == to_mflpt5(0.7071067811865476)
assert b"\x81\x35\x04\xF3\x34" == to_mflpt5(1.4142135624)
assert b"\x81\x35\x04\xF3\x33" == to_mflpt5(1.4142135623730951)
assert b"\x80\x80\x00\x00\x00" == to_mflpt5(-.5)
assert b"\x80\x31\x72\x17\xF8" == to_mflpt5(0.69314718061)
assert b"\x80\x31\x72\x17\xF7" == to_mflpt5(0.6931471805599453)
assert b"\x84\x20\x00\x00\x00" == to_mflpt5(10)
assert b"\x9E\x6E\x6B\x28\x00" == to_mflpt5(1000000000)
assert b"\x80\x00\x00\x00\x00" == to_mflpt5(.5)
assert b"\x81\x38\xAA\x3B\x29" == to_mflpt5(1.4426950408889634)
assert b"\x81\x49\x0F\xDA\xA2" == to_mflpt5(1.5707963267948966)
assert b"\x83\x49\x0F\xDA\xA2" == to_mflpt5(6.283185307179586)
assert b"\x7F\x00\x00\x00\x00" == to_mflpt5(.25)
def test_float_range():
assert b"\xff\x7f\xff\xff\xff" == to_mflpt5(FLOAT_MAX_POSITIVE)
assert b"\xff\xff\xff\xff\xff" == to_mflpt5(FLOAT_MAX_NEGATIVE)
with pytest.raises(OverflowError):
to_mflpt5(1.7014118346e+38)
with pytest.raises(OverflowError):
to_mflpt5(-1.7014118346e+38)
with pytest.raises(OverflowError):
to_mflpt5(1.7014118347e+38)
with pytest.raises(OverflowError):
to_mflpt5(-1.7014118347e+38)
assert b"\x03\x39\x1d\x15\x63" == to_mflpt5(1.7e-38)
assert b"\x00\x00\x00\x00\x00" == to_mflpt5(1.7e-39)
assert b"\x03\xb9\x1d\x15\x63" == to_mflpt5(-1.7e-38)
assert b"\x00\x00\x00\x00\x00" == to_mflpt5(-1.7e-39)
def test_char_to_bytevalue():
assert char_to_bytevalue('a') == 65
assert char_to_bytevalue('\n') == 13

View File

@ -8,7 +8,7 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
import enum
import struct
from typing import Callable
from il65.emit import mflpt5_to_float, to_mflpt5
from il65.codegen.shared import mflpt5_to_float, to_mflpt5
class DataType(enum.IntEnum):

View File

@ -23,14 +23,12 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
# or in one of the dynamic variables.
#
# I/O: either via programmed I/O routines:
# write [byte/bytearray to text output/screen],
# read [byte/bytearray from keyboard],
# wait [till any input comes available], @todo
# check [if input is available) @todo
# write [byte/bytearray to text output/screen] : syscall_printstr / syscall_printchr,
# read [byte/bytearray from keyboard] : syscall_input / syscall_getchr (both blocking)
# or via memory-mapped I/O (text screen matrix, keyboard scan register)
#
# CPU: stack based execution, no registers.
# unlimited dynamic variables (v0, v1, ...) that have a value and a type.
# CPU: single threaded, stack based execution,
# no registers, but unlimited dynamic variables (v0, v1, ...) that have a value and a type.
# types:
# 1-bit boolean,
# 8-bit byte (singed and unsigned),
@ -41,9 +39,6 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
# matrix (2-dimensional array) of bytes (signed and unsigned).
# all of these can have the flag CONST as well which means they cannot be modified.
#
# push (constant,
# mark, unwind to previous mark.
#
# CPU INSTRUCTIONS:
# stack manipulation mainly:
# nop
@ -53,12 +48,11 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
# jump label
# jump_if_true label, jump_if_false label
# jump_if_status_XX label special system dependent status register conditional check such as carry bit or overflow bit)
# call function (arguments are on stack)
# return (return values on stack)
# syscall function (special system dependent implementation)
# call function (arguments are on stack)
# enter / exit (function call frame)
#
# TIMER INTERRUPT: triggered around each 1/60th of a second.
# TIMER 'INTERRUPT': triggered around each 1/60th of a second.
# executes on a DIFFERENT stack and with a different PROGRAM LIST,
# but with access to ALL THE SAME DYNAMIC VARIABLES.
# This suspends the main program until the timer program RETURNs!