diff --git a/arch/zx48k/backend/__common.py b/arch/zx48k/backend/__common.py index 5ab300caf..21dc518e1 100644 --- a/arch/zx48k/backend/__common.py +++ b/arch/zx48k/backend/__common.py @@ -41,13 +41,13 @@ def init(): TMP_LABELS.clear() -def log2(x): +def log2(x) -> float: """ Returns log2(x) """ return math.log(x) / __LN2 -def is_2n(x): +def is_2n(x) -> bool: """ Returns true if x is an exact power of 2 """ @@ -55,7 +55,7 @@ def is_2n(x): return l == int(l) -def tmp_label(): +def tmp_label() -> str: global LABEL_COUNTER global TMP_LABELS @@ -66,7 +66,7 @@ def tmp_label(): return result -def tmp_temp(): +def tmp_temp() -> str: global TMP_COUNTER for i in range(TMP_COUNTER): diff --git a/arch/zx48k/translator.py b/arch/zx48k/translator.py index 2dad6c53e..e1c9bbd33 100644 --- a/arch/zx48k/translator.py +++ b/arch/zx48k/translator.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from collections import OrderedDict from collections import namedtuple from api.constants import TYPE @@ -16,23 +15,18 @@ from api.debug import __DEBUG__ from api.errmsg import syntax_error -from api.errmsg import syntax_error_not_constant -from api.errmsg import syntax_error_cant_convert_to_type from api.config import OPTIONS from api.global_ import optemps from api.errors import InvalidLoopError from api.errors import InvalidOperatorError -from api.errors import InvalidCONSTexpr from api.errors import InvalidBuiltinFunctionError -from symbols.symbol_ import Symbol from . import backend -from .backend import Quad, MEMORY -from ast_ import NodeVisitor from .backend.__float import _float import symbols from symbols.type_ import Type +from .translatorvisitor import TranslatorVisitor import arch.zx48k # TODO: put this in global @@ -43,277 +37,14 @@ JumpTable = namedtuple('JumpTable', ('label', 'addresses')) -class TranslatorVisitor(NodeVisitor): - """ This visitor just adds the emit() method. - """ - # ------------------------------------------------ - # A list of tokens that belongs to temporary - # ATTR setting - # ------------------------------------------------ - ATTR = ('INK', 'PAPER', 'BRIGHT', 'FLASH', 'OVER', 'INVERSE', 'BOLD', 'ITALIC') - ATTR_TMP = tuple(x + '_TMP' for x in ATTR) - - # Local flags - HAS_ATTR = False - - # Previous Token - PREV_TOKEN = None - - # Current Token - CURR_TOKEN = None - - LOOPS = [] # Defined LOOPS - STRING_LABELS = OrderedDict() - JUMP_TABLES = [] - - # Type code used in DATA - DATA_TYPES = { - 'str': 1, - 'i8': 2, - 'u8': 3, - 'i16': 4, - 'u16': 5, - 'i32': 6, - 'u32': 7, - 'f16': 8, - 'f': 9 - } - - @classmethod - def reset(cls): - cls.LOOPS = [] # Defined LOOPS - cls.STRING_LABELS = OrderedDict() - cls.JUMP_TABLES = [] - - def add_string_label(self, str_): - """ Maps ("folds") the given string, returning an unique label ID. - This allows several constant labels to be initialized to the same address - thus saving memory space. - :param str_: the string to map - :return: the unique label ID - """ - if self.STRING_LABELS.get(str_, None) is None: - self.STRING_LABELS[str_] = backend.tmp_label() - - return self.STRING_LABELS[str_] - - @property - def O_LEVEL(self): - return OPTIONS.optimization.value - - @staticmethod - def TYPE(type_): - """ Converts a backend type (from api.constants) - to a SymbolTYPE object (taken from the SYMBOL_TABLE). - If type_ is already a SymbolTYPE object, nothing - is done. - """ - if isinstance(type_, symbols.TYPE): - return type_ - - assert TYPE.is_valid(type_) - return gl.SYMBOL_TABLE.basic_types[type_] - - @staticmethod - def TSUFFIX(type_): - assert isinstance(type_, symbols.TYPE) or TYPE.is_valid(type_) - - _TSUFFIX = {TYPE.byte_: 'i8', TYPE.ubyte: 'u8', - TYPE.integer: 'i16', TYPE.uinteger: 'u16', - TYPE.long_: 'i32', TYPE.ulong: 'u32', - TYPE.fixed: 'f16', TYPE.float_: 'f', - TYPE.string: 'str' - } - - if isinstance(type_, symbols.TYPEREF): - type_ = type_.final - assert isinstance(type_, symbols.BASICTYPE) - - if isinstance(type_, symbols.BASICTYPE): - return _TSUFFIX[type_.type_] - - return _TSUFFIX[type_] - - @staticmethod - def emit(*args): - """ Convert the given args to a Quad (3 address code) instruction - """ - quad = Quad(*args) - __DEBUG__('EMIT ' + str(quad)) - MEMORY.append(quad) - - @staticmethod - def dumpMemory(MEMORY): - """ Returns a sequence of Quads - """ - for x in MEMORY: - yield str(x) - - # Generic Visitor methods - def visit_BLOCK(self, node): - __DEBUG__('BLOCK', 2) - for child in node.children: - yield child - - # Visits any temporal attribute - def visit_ATTR_TMP(self, node): - yield node.children[0] - self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) - self.emit('call', node.token, 0) # Procedure call. Discard return - ifile = node.token.lower() - ifile = ifile[:ifile.index('_')] - backend.REQUIRES.add(ifile + '.asm') - - # This function must be called before emit_strings - def emit_data_blocks(self): - if not gl.DATA_IS_USED: - return # nothing to do - - for label_, datas in gl.DATAS: - self.emit('label', label_) - for d in datas: - if isinstance(d, symbols.FUNCDECL): - type_ = '%02Xh' % (self.DATA_TYPES[self.TSUFFIX(d.type_)] | 0x80) - self.emit('data', self.TSUFFIX(TYPE.byte_), [type_]) - self.emit('data', self.TSUFFIX(gl.PTR_TYPE), [d.mangled]) - continue - - self.emit('data', self.TSUFFIX(TYPE.byte_), [self.DATA_TYPES[self.TSUFFIX(d.value.type_)]]) - if d.value.type_ == self.TYPE(TYPE.string): - lbl = self.add_string_label(d.value.value) - self.emit('data', self.TSUFFIX(api.global_.PTR_TYPE), [lbl]) - elif d.value.type_ == self.TYPE(TYPE.fixed): # Convert to bytes - bytes_ = 0xFFFFFFFF & int(d.value.value * 2 ** 16) - self.emit('data', self.TSUFFIX(TYPE.uinteger), - ['0x%04X' % (bytes_ & 0xFFFF), '0x%04X' % (bytes_ >> 16)]) - else: - self.emit('data', self.TSUFFIX(d.value.type_), [self.traverse_const(d.value)]) - - if not gl.DATAS: # The above loop was not executed, because there's no data - self.emit('label', '__DATA__0') - - self.emit('vard', '__DATA__END', ['00']) - - def emit_strings(self): - for str_, label_ in self.STRING_LABELS.items(): - l = '%04X' % (len(str_) & 0xFFFF) # TODO: Universalize for any arch - self.emit('vard', label_, [l] + ['%02X' % ord(x) for x in str_]) - - def emit_jump_tables(self): - for table_ in self.JUMP_TABLES: - self.emit('vard', table_.label, [str(len(table_.addresses))] + - ['##' + x.mangled for x in table_.addresses]) - - def _visit(self, node): - self.norm_attr() - if isinstance(node, Symbol): - __DEBUG__('Visiting {}'.format(node.token), 1) - if node.token in self.ATTR_TMP: - return self.visit_ATTR_TMP(node) - - return NodeVisitor._visit(self, node) - - def norm_attr(self): - """ Normalize attr state - """ - if not self.HAS_ATTR: - return - - self.HAS_ATTR = False - self.emit('call', 'COPY_ATTR', 0) - backend.REQUIRES.add('copy_attr.asm') - - @staticmethod - def traverse_const(node): - """ Traverses a constant and returns an string - with the arithmetic expression - """ - if node.token == 'NUMBER': - return node.t - - if node.token == 'UNARY': - mid = node.operator - if mid == 'MINUS': - result = ' -' + Translator.traverse_const(node.operand) - elif mid == 'ADDRESS': - if node.operand.scope == SCOPE.global_ or node.operand.token in ('LABEL', 'FUNCTION'): - result = Translator.traverse_const(node.operand) - else: - syntax_error_not_constant(node.operand.lineno) - return - else: - raise InvalidOperatorError(mid) - return result - - if node.token == 'BINARY': - mid = node.operator - if mid == 'PLUS': - mid = '+' - elif mid == 'MINUS': - mid = '-' - elif mid == 'MUL': - mid = '*' - elif mid == 'DIV': - mid = '/' - elif mid == 'MOD': - mid = '%' - elif mid == 'POW': - mid = '^' - elif mid == 'SHL': - mid = '>>' - elif mid == 'SHR': - mid = '<<' - else: - raise InvalidOperatorError(mid) - - return '(%s) %s (%s)' % (Translator.traverse_const(node.left), mid, Translator.traverse_const(node.right)) - - if node.token == 'TYPECAST': - if node.type_ in (Type.byte_, Type.ubyte): - return '(' + Translator.traverse_const(node.operand) + ') & 0xFF' - if node.type_ in (Type.integer, Type.uinteger): - return '(' + Translator.traverse_const(node.operand) + ') & 0xFFFF' - if node.type_ in (Type.long_, Type.ulong): - return '(' + Translator.traverse_const(node.operand) + ') & 0xFFFFFFFF' - if node.type_ == Type.fixed: - return '((' + Translator.traverse_const(node.operand) + ') & 0xFFFF) << 16' - syntax_error_cant_convert_to_type(node.lineno, str(node.operand), node.type_) - return - - if node.token == 'VARARRAY': - return node.data_label - - if node.token in ('VAR', 'LABEL', 'FUNCTION'): - # TODO: Check what happens with local vars and params - return node.t - - if node.token == 'CONST': - return Translator.traverse_const(node.expr) - - if node.token == 'ARRAYACCESS': - return '({} + {})'.format(node.entry.data_label, node.offset) - - raise InvalidCONSTexpr(node) - - @staticmethod - def check_attr(node, n): - """ Check if ATTR has to be normalized - after this instruction has been translated - to intermediate code. - """ - if len(node.children) > n: - return node.children[n] - - class Translator(TranslatorVisitor): """ ZX Spectrum translator """ - def visit_NOP(self, node): pass # nothing to do def visit_CLS(self, node): - self.emit('call', 'CLS', 0) + self.ic_call('CLS', 0) backend.REQUIRES.add('cls.asm') def visit_NUMBER(self, node): @@ -328,22 +59,22 @@ def visit_STRING(self, node): def visit_END(self, node): yield node.children[0] __DEBUG__('END') - self.emit('end', node.children[0].t) + self.ic_end(node.children[0].t) def visit_ERROR(self, node): # Raises an error yield node.children[0] - self.emit('fparamu8', node.children[0].t) - self.emit('call', '__ERROR', 0) + self.ic_fparam(TYPE.ubyte, node.children[0].t) + self.ic_call('__ERROR', 0) backend.REQUIRES.add('error.asm') def visit_STOP(self, node): """ Returns to BASIC with an error code """ yield node.children[0] - self.emit('fparamu8', node.children[0].t) - self.emit('call', '__STOP', 0) - self.emit('end', 0) + self.ic_fparam(TYPE.ubyte, node.children[0].t) + self.ic_call('__STOP', 0) + self.ic_end(0) backend.REQUIRES.add('error.asm') def visit_LET(self, node): @@ -360,20 +91,20 @@ def visit_POKE(self, node): yield ch1 if ch0.token == 'VAR' and ch0.class_ != CLASS.const and ch0.scope == SCOPE.global_: - self.emit('store' + self.TSUFFIX(ch1.type_), '*' + str(ch0.t), ch1.t) + self.ic_store(ch1.type_, '*' + str(ch0.t), ch1.t) else: - self.emit('store' + self.TSUFFIX(ch1.type_), ch0.t, ch1.t) + self.ic_store(ch1.type_, str(ch0.t), ch1.t) def visit_RANDOMIZE(self, node): yield node.children[0] - self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) - self.emit('call', 'RANDOMIZE', 0) + self.ic_fparam(node.children[0].type_, node.children[0].t) + self.ic_call('RANDOMIZE', 0) backend.REQUIRES.add('random.asm') def visit_LABEL(self, node): - self.emit('label', node.mangled) + self.ic_label(node.mangled) for tmp in node.aliased_by: - self.emit('label', tmp.mangled) + self.ic_label(tmp.mangled) def visit_VAR(self, node): __DEBUG__('{}: VAR {}:{} Scope: {} Class: {}'.format(node.lineno, node.name, node.type_, @@ -386,20 +117,16 @@ def visit_VAR(self, node): if node.class_ in (CLASS.label, CLASS.const): return - suffix = self.TSUFFIX(node.type_) p = '*' if node.byref else '' # Indirection prefix - alias = node.alias - if scope == SCOPE.global_: - self.emit('load' + suffix, node.t, node.mangled) - elif scope == SCOPE.parameter: - self.emit('pload' + suffix, node.t, p + str(node.offset)) + if scope == SCOPE.parameter: + self.ic_pload(node.type_, node.t, p + str(node.offset)) elif scope == SCOPE.local: offset = node.offset - if alias is not None and alias.class_ == CLASS.array: - offset -= 1 + 2 * alias.count + if node.alias is not None and node.alias.class_ == CLASS.array: + offset -= 1 + 2 * node.alias.count # TODO this is actually NOT implemented - self.emit('pload' + suffix, node.t, p + str(-offset)) + self.ic_pload(node.type_, node.t, p + str(-offset)) def visit_CONST(self, node): node.t = '#' + (self.traverse_const(node) or '') @@ -443,8 +170,7 @@ def visit_TYPECAST(self, node): yield node.operand assert node.operand.type_.is_basic assert node.type_.is_basic - self.emit('cast', node.t, self.TSUFFIX(node.operand.type_.type_), - self.TSUFFIX(node.type_.type_), node.operand.t) + self.ic_cast(node.t, node.operand.type_, node.type_, node.operand.t) def visit_FUNCDECL(self, node): # Delay emission of functions until the end of the main code @@ -454,11 +180,11 @@ def visit_CALL(self, node): yield node.args # arglist if node.entry.convention == CONVENTION.fastcall: if len(node.args) > 0: # At least 1 parameter - self.emit('fparam' + self.TSUFFIX(node.args[0].type_), optemps.new_t()) + self.ic_fparam(node.args[0].type_, optemps.new_t()) - self.emit('call', node.entry.mangled, 0) # Procedure call. 0 = discard return + self.ic_call(node.entry.mangled, 0) # Procedure call. 0 = discard return if node.entry.kind == KIND.function and node.entry.type_ == self.TYPE(TYPE.string): - self.emit('call', '__MEM_FREE', 0) # Discard string return value if the called function has any + self.ic_call('__MEM_FREE', 0) # Discard string return value if the called function has any backend.REQUIRES.add('free.asm') def visit_ARGLIST(self, node): @@ -468,22 +194,21 @@ def visit_ARGLIST(self, node): if isinstance(node.parent, symbols.ARRAYACCESS) and OPTIONS.arrayCheck.value: upper = node.parent.entry.bounds[i].upper lower = node.parent.entry.bounds[i].lower - self.emit('paramu16', upper - lower) + self.ic_param(gl.PTR_TYPE, upper - lower) def visit_ARGUMENT(self, node): if not node.byref: - suffix = self.TSUFFIX(node.type_) if node.value.token in ('VAR', 'PARAMDECL') and \ node.type_.is_dynamic and node.value.t[0] == '$': # Duplicate it in the heap assert (node.value.scope in (SCOPE.local, SCOPE.parameter)) if node.value.scope == SCOPE.local: - self.emit('pload' + suffix, node.t, str(-node.value.offset)) + self.ic_pload(node.type_, node.t, str(-node.value.offset)) else: # PARAMETER - self.emit('pload' + suffix, node.t, str(node.value.offset)) + self.ic_pload(node.type_, node.t, str(node.value.offset)) else: yield node.value - self.emit('param' + suffix, node.t) + self.ic_param(node.type_, node.t) else: scope = node.value.scope if node.t[0] == '_': @@ -492,16 +217,16 @@ def visit_ARGUMENT(self, node): t = node.t if scope == SCOPE.global_: - self.emit('loadu16', t, '#' + node.mangled) + self.ic_load(TYPE.uinteger, t, '#' + node.mangled) elif scope == SCOPE.parameter: # A function has used a parameter as an argument to another function call if not node.value.byref: # It's like a local variable - self.emit('paddr', node.value.offset, t) + self.ic_paddr(node.value.offset, t) else: - self.emit('ploadu16', t, str(node.value.offset)) + self.ic_pload(TYPE.uinteger, t, str(node.value.offset)) elif scope == SCOPE.local: - self.emit('paddr', -node.value.offset, t) + self.ic_paddr(-node.value.offset, t) - self.emit('paramu16', t) + self.ic_param(TYPE.uinteger, t) def visit_ARRAYLOAD(self, node): scope = node.entry.scope @@ -510,25 +235,24 @@ def visit_ARRAYLOAD(self, node): yield node.args if scope == SCOPE.global_: - self.emit('aload' + self.TSUFFIX(node.type_), node.entry.t, node.entry.mangled) + self.ic_aload(node.type_, node.entry.t, node.entry.mangled) elif scope == SCOPE.parameter: - self.emit('paload' + self.TSUFFIX(node.type_), node.t, node.entry.offset) + self.ic_paload(node.type_, node.t, node.entry.offset) elif scope == SCOPE.local: - self.emit('paload' + self.TSUFFIX(node.type_), node.t, -node.entry.offset) + self.ic_paload(node.type_, node.t, -node.entry.offset) else: offset = node.offset if scope == SCOPE.global_: - self.emit('load' + self.TSUFFIX(node.type_), node.entry.t, '%s + %i' % (node.entry.t, offset)) + self.ic_load(node.type_, node.entry.t, '%s + %i' % (node.entry.t, offset)) elif scope == SCOPE.parameter: - self.emit('pload' + self.TSUFFIX(node.type_), node.t, node.entry.offset - offset) + self.ic_pload(node.type_, node.t, node.entry.offset - offset) elif scope == SCOPE.local: t1 = optemps.new_t() t2 = optemps.new_t() t3 = optemps.new_t() - self.emit('pload' + self.TSUFFIX(gl.PTR_TYPE), t1, - -(node.entry.offset - self.TYPE(gl.PTR_TYPE).size)) - self.emit('add' + self.TSUFFIX(gl.PTR_TYPE), t2, t1, node.offset) - self.emit('load' + self.TSUFFIX(node.type_), t3, '*$%s' % t2) + self.ic_pload(gl.PTR_TYPE, t1, -(node.entry.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_add(gl.PTR_TYPE, t2, t1, node.offset) + self.ic_load(node.type_, t3, '*$%s' % t2) def visit_ARRAYCOPY(self, node): tr = node.children[0] @@ -537,10 +261,10 @@ def visit_ARRAYCOPY(self, node): t1 = "#%s" % tr.data_label elif scope == SCOPE.parameter: t1 = optemps.new_t() - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t1, '%i' % (tr.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_pload(gl.PTR_TYPE, t1, '%i' % (tr.offset - self.TYPE(gl.PTR_TYPE).size)) elif scope == SCOPE.local: t1 = optemps.new_t() - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t1, '%i' % -(tr.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_pload(gl.PTR_TYPE, t1, '%i' % -(tr.offset - self.TYPE(gl.PTR_TYPE).size)) tr = node.children[1] scope = tr.scope @@ -548,18 +272,18 @@ def visit_ARRAYCOPY(self, node): t2 = "#%s" % tr.data_label elif scope == SCOPE.parameter: t2 = optemps.new_t() - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t2, '%i' % (tr.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_pload(gl.PTR_TYPE, t2, '%i' % (tr.offset - self.TYPE(gl.PTR_TYPE).size)) elif scope == SCOPE.local: t2 = optemps.new_t() - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t2, '%i' % -(tr.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_pload(gl.PTR_TYPE, t2, '%i' % -(tr.offset - self.TYPE(gl.PTR_TYPE).size)) t = optemps.new_t() if tr.type_ != Type.string: - self.emit('load%s' % self.TSUFFIX(gl.PTR_TYPE), t, '%i' % tr.size) - self.emit('memcopy', t1, t2, t) + self.ic_load(gl.PTR_TYPE, t, '%i' % tr.size) + self.ic_memcopy(t1, t2, t) else: - self.emit('load%s' % self.TSUFFIX(gl.PTR_TYPE), '%i' % tr.count) - self.emit('call', 'STR_ARRAYCOPY', 0) + self.ic_load(gl.PTR_TYPE, '%i' % tr.count) + self.ic_call('STR_ARRAYCOPY', 0) backend.REQUIRES.add('strarraycpy.asm') def visit_LETARRAY(self, node): @@ -569,43 +293,41 @@ def visit_LETARRAY(self, node): yield node.children[1] # Right expression arr = node.children[0] # Array access scope = arr.scope - suf = self.TSUFFIX(arr.type_) if arr.offset is None: yield arr if scope == SCOPE.global_: - self.emit('astore' + suf, arr.entry.mangled, node.children[1].t) + self.ic_astore(arr.type_, arr.entry.mangled, node.children[1].t) elif scope == SCOPE.parameter: - self.emit('pastore' + suf, arr.entry.offset, node.children[1].t) + self.ic_pastore(arr.type_, arr.entry.offset, node.children[1].t) elif scope == SCOPE.local: - self.emit('pastore' + suf, -arr.entry.offset, node.children[1].t) + self.ic_pastore(arr.type_, -arr.entry.offset, node.children[1].t) else: name = arr.entry.data_label if scope == SCOPE.global_: - self.emit('store' + suf, '%s + %i' % (name, arr.offset), node.children[1].t) + self.ic_store(arr.type_, '%s + %i' % (name, arr.offset), node.children[1].t) elif scope == SCOPE.parameter: - self.emit('pstore' + suf, arr.entry.offset - arr.offset, node.children[1].t) + self.ic_pstore(arr.type_, arr.entry.offset - arr.offset, node.children[1].t) elif scope == SCOPE.local: - self.emit('pstore' + suf, -(arr.entry.offset - arr.offset), node.children[1].t) + self.ic_pstore(arr.type_, -(arr.entry.offset - arr.offset), node.children[1].t) def visit_LETSUBSTR(self, node): yield node.children[3] - self.emit('paramstr', node.children[3].t) + self.ic_param(TYPE.string, node.children[3].t) if node.children[3].token != 'STRING' and (node.children[3].token != 'VAR' or node.children[3].mangled[0] != '_'): - self.emit('paramu8', 1) # If the argument is not a variable, it must be freed + self.ic_param(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed else: - self.emit('paramu8', 0) + self.ic_param(TYPE.ubyte, 0) yield node.children[1] - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[1].t) + self.ic_param(gl.PTR_TYPE, node.children[1].t) yield node.children[2] - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[2].t) - - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.children[0].t) - self.emit('call', '__LETSUBSTR', 0) + self.ic_param(gl.PTR_TYPE, node.children[2].t) + self.ic_fparam(gl.PTR_TYPE, node.children[0].t) + self.ic_call('__LETSUBSTR', 0) backend.REQUIRES.add('letsubstr.asm') def visit_LETARRAYSUBSTR(self, node): @@ -614,43 +336,42 @@ def visit_LETARRAYSUBSTR(self, node): expr = node.children[3] # right expression yield expr - self.emit('paramstr', expr.t) + self.ic_param(TYPE.string, expr.t) if expr.token != 'STRING' and (expr.token != 'VAR' or expr.mangled[0] != '_'): - self.emit('paramu8', 1) # If the argument is not a variable, it must be freed + self.ic_param(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed else: - self.emit('paramu8', 0) + self.ic_param(TYPE.ubyte, 0) yield node.children[1] - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[1].t) + self.ic_param(gl.PTR_TYPE, node.children[1].t) yield node.children[2] - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[2].t) + self.ic_param(gl.PTR_TYPE, node.children[2].t) node_ = node.children[0] scope = node_.scope entry = node_.entry - suffix = self.TSUFFIX(gl.PTR_TYPE) # Address of an array element. if node_.offset is None: yield node_ if scope == SCOPE.global_: - self.emit('aload' + suffix, node_.t, entry.mangled) - elif scope == 'parameter': - self.emit('paloadstr' + suffix, node_.t, entry.offset) - elif scope == 'local': - self.emit('paloadstr' + suffix, node_.t, -entry.offset) + self.ic_aload(gl.PTR_TYPE, node_.t, entry.mangled) + elif scope == SCOPE.parameter: # TODO: These 2 are never used!?? + self.ic_paload(gl.PTR_TYPE, node_.t, entry.offset) + elif scope == SCOPE.local: + self.ic_paload(gl.PTR_TYPE, node_.t, -entry.offset) else: offset = node_.offset if scope == SCOPE.global_: - self.emit('load' + suffix, entry.t, '%s + %i' % (entry.mangled, offset)) + self.ic_load(gl.PTR_TYPE, entry.t, '%s + %i' % (entry.mangled, offset)) elif scope == SCOPE.parameter: - self.emit('pload' + suffix, node_.t, entry.offset - offset) + self.ic_pload(gl.PTR_TYPE, node_.t, entry.offset - offset) elif scope == SCOPE.local: - self.emit('pload' + suffix, node_.t, -(entry.offset - offset)) + self.ic_pload(gl.PTR_TYPE, node_.t, -(entry.offset - offset)) - self.emit('fparam' + suffix, node.children[0].t) - self.emit('call', '__LETSUBSTR', 0) + self.ic_fparam(gl.PTR_TYPE, node.children[0].t) + self.ic_call('__LETSUBSTR', 0) backend.REQUIRES.add('letsubstr.asm') def visit_ARRAYACCESS(self, node): @@ -660,22 +381,22 @@ def visit_STRSLICE(self, node): yield node.string if node.string.token == 'STRING' or \ node.string.token == 'VAR' and node.string.scope == SCOPE.global_: - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.string.t) + self.ic_param(gl.PTR_TYPE, node.string.t) # Now emit the slicing indexes yield node.lower - self.emit('param' + self.TSUFFIX(node.lower.type_), node.lower.t) + self.ic_param(node.lower.type_, node.lower.t) yield node.upper - self.emit('param' + self.TSUFFIX(node.upper.type_), node.upper.t) + self.ic_param(node.upper.type_, node.upper.t) if (node.string.token in ('VAR', 'PARAMDECL') and node.string.mangled[0] == '_' or node.string.token == 'STRING'): - self.emit('fparamu8', 0) + self.ic_fparam(TYPE.ubyte, 0) else: - self.emit('fparamu8', 1) # If the argument is not a variable, it must be freed + self.ic_fparam(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed - self.emit('call', '__STRSLICE', self.TYPE(gl.PTR_TYPE).size) + self.ic_call('__STRSLICE', self.TYPE(gl.PTR_TYPE).size) backend.REQUIRES.add('strslice.asm') def visit_FUNCCALL(self, node): @@ -683,46 +404,44 @@ def visit_FUNCCALL(self, node): if node.entry.convention == CONVENTION.fastcall: if len(node.args) > 0: # At least 1 - self.emit('fparam' + self.TSUFFIX(node.args[0].type_), optemps.new_t()) + self.ic_fparam(node.args[0].type_, optemps.new_t()) - self.emit('call', node.entry.mangled, node.entry.size) + self.ic_call(node.entry.mangled, node.entry.size) def visit_RESTORE(self, node): if not gl.DATA_IS_USED: return # If no READ is used, ignore all DATA related statements lbl = gl.DATA_LABELS[node.args[0].name] - self.emit('fparam' + self.TSUFFIX(node.args[0].type_), '#' + lbl) - self.emit('call', '__RESTORE', 0) + self.ic_fparam(node.args[0].type_, '#' + lbl) + self.ic_call('__RESTORE', 0) backend.REQUIRES.add('read_restore.asm') def visit_READ(self, node): - self.emit('fparamu8', '#' + str(self.DATA_TYPES[self.TSUFFIX(node.args[0].type_)])) - self.emit('call', '__READ', node.args[0].type_.size) + self.ic_fparam(TYPE.ubyte, '#' + str(self.DATA_TYPES[self.TSUFFIX(node.args[0].type_)])) + self.ic_call('__READ', node.args[0].type_.size) if isinstance(node.args[0], symbols.ARRAYACCESS): arr = node.args[0] t = api.global_.optemps.new_t() scope = arr.scope - suf = self.TSUFFIX(arr.type_) if arr.offset is None: yield arr if scope == SCOPE.global_: - self.emit('astore' + suf, arr.entry.mangled, t) + self.ic_astore(arr.type_, arr.entry.mangled, t) elif scope == SCOPE.parameter: - self.emit('pastore' + suf, arr.entry.offset, t) + self.ic_pastore(arr.type_, arr.entry.offset, t) elif scope == SCOPE.local: - self.emit('pastore' + suf, -arr.entry.offset, t) + self.ic_pastore(arr.type_, -arr.entry.offset, t) else: name = arr.entry.mangled if scope == SCOPE.global_: - self.emit('store' + suf, '%s + %i' % (name, arr.offset), t) + self.ic_store(arr.type_, '%s + %i' % (name, arr.offset), t) elif scope == SCOPE.parameter: - self.emit('pstore' + suf, arr.entry.offset - arr.offset, t) + self.ic_pstore(arr.type_, arr.entry.offset - arr.offset, t) elif scope == SCOPE.local: - self.emit('pstore' + suf, -(arr.entry.offset - arr.offset), t) - + self.ic_pstore(arr.type_, -(arr.entry.offset - arr.offset), t) else: self.emit_var_assign(node.args[0], t=api.global_.optemps.new_t()) backend.REQUIRES.add('read_restore.asm') @@ -736,12 +455,12 @@ def visit_DO_LOOP(self, node): end_loop = backend.tmp_label() self.LOOPS.append(('DO', end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE - self.emit('label', loop_label) + self.ic_label(loop_label) if node.children: yield node.children[0] - self.emit('jump', loop_label) - self.emit('label', end_loop) + self.ic_jump(loop_label) + self.ic_label(end_loop) self.LOOPS.pop() # del loop_label, end_loop @@ -754,38 +473,38 @@ def visit_DO_WHILE(self, node): continue_loop = backend.tmp_label() if node.token == 'WHILE_DO': - self.emit('jump', continue_loop) + self.ic_jump(continue_loop) - self.emit('label', loop_label) + self.ic_label(loop_label) self.LOOPS.append(('DO', end_loop, continue_loop)) # Saves which labels to jump upon EXIT or CONTINUE if len(node.children) > 1: yield node.children[1] - self.emit('label', continue_loop) + self.ic_label(continue_loop) yield node.children[0] - self.emit('jnzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, loop_label) - self.emit('label', end_loop) + self.ic_jnzero(node.children[0].type_, node.children[0].t, loop_label) + self.ic_label(end_loop) self.LOOPS.pop() # del loop_label, end_loop, continue_loop def visit_EXIT_DO(self, node): - self.emit('jump', self.loop_exit_label('DO')) + self.ic_jump(self.loop_exit_label('DO')) def visit_EXIT_WHILE(self, node): - self.emit('jump', self.loop_exit_label('WHILE')) + self.ic_jump(self.loop_exit_label('WHILE')) def visit_EXIT_FOR(self, node): - self.emit('jump', self.loop_exit_label('FOR')) + self.ic_jump(self.loop_exit_label('FOR')) def visit_CONTINUE_DO(self, node): - self.emit('jump', self.loop_cont_label('DO')) + self.ic_jump(self.loop_cont_label('DO')) def visit_CONTINUE_WHILE(self, node): - self.emit('jump', self.loop_cont_label('WHILE')) + self.ic_jump(self.loop_cont_label('WHILE')) def visit_CONTINUE_FOR(self, node): - self.emit('jump', self.loop_cont_label('FOR')) + self.ic_jump(self.loop_cont_label('FOR')) def visit_FOR(self, node): loop_label_start = backend.tmp_label() @@ -793,30 +512,30 @@ def visit_FOR(self, node): end_loop = backend.tmp_label() loop_body = backend.tmp_label() loop_continue = backend.tmp_label() - suffix = self.TSUFFIX(node.children[0].type_) + type_ = node.children[0].type_ self.LOOPS.append(('FOR', end_loop, loop_continue)) # Saves which label to jump upon EXIT FOR and CONTINUE FOR yield node.children[1] # Gets starting value (lower limit) self.emit_let_left_part(node) # Stores it in the iterator variable - self.emit('jump', loop_label_start) + self.ic_jump(loop_label_start) # FOR body statements - self.emit('label', loop_body) + self.ic_label(loop_body) yield node.children[4] # Jump here to continue next iteration - self.emit('label', loop_continue) + self.ic_label(loop_continue) # VAR = VAR + STEP yield node.children[0] # Iterator Var yield node.children[3] # Step t = optemps.new_t() - self.emit('add' + suffix, t, node.children[0].t, node.children[3].t) + self.ic_add(type_, t, node.children[0].t, node.children[3].t) self.emit_let_left_part(node, t) # Loop starts here - self.emit('label', loop_label_start) + self.ic_label(loop_label_start) # Emmit condition if check.is_number(node.children[3]) or check.is_unsigned(node.children[3].type_): @@ -824,58 +543,58 @@ def visit_FOR(self, node): else: direct = False yield node.children[3] # Step - self.emit('jgezero' + suffix, node.children[3].t, loop_label_gt) + self.ic_jgezero(type_, node.children[3].t, loop_label_gt) if not direct or node.children[3].value < 0: # Here for negative steps # Compares if var < limit2 yield node.children[0] # Value of var yield node.children[2] # Value of limit2 - self.emit('lt' + suffix, node.t, node.children[0].t, node.children[2].t) - self.emit('jzerou8', node.t, loop_body) + self.ic_lt(type_, node.t, node.children[0].t, node.children[2].t) + self.ic_jzero(TYPE.ubyte, node.t, loop_body) if not direct: - self.emit('jump', end_loop) - self.emit('label', loop_label_gt) + self.ic_jump(end_loop) + self.ic_label(loop_label_gt) if not direct or node.children[3].value >= 0: # Here for positive steps # Compares if var > limit2 yield node.children[0] # Value of var yield node.children[2] # Value of limit2 - self.emit('gt' + suffix, node.t, node.children[0].t, node.children[2].t) - self.emit('jzerou8', node.t, loop_body) + self.ic_gt(type_, node.t, node.children[0].t, node.children[2].t) + self.ic_jzero(TYPE.ubyte, node.t, loop_body) - self.emit('label', end_loop) + self.ic_label(end_loop) self.LOOPS.pop() def visit_GOTO(self, node): - self.emit('jump', node.children[0].mangled) + self.ic_jump(node.children[0].mangled) def visit_GOSUB(self, node): - self.emit('call', node.children[0].mangled, 0) + self.ic_call(node.children[0].mangled, 0) def visit_ON_GOTO(self, node): table_label = backend.tmp_label() - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), '#' + table_label) + self.ic_param(gl.PTR_TYPE, '#' + table_label) yield node.children[0] - self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) - self.emit('call', '__ON_GOTO', 0) + self.ic_fparam(node.children[0].type_, node.children[0].t) + self.ic_call('__ON_GOTO', 0) self.JUMP_TABLES.append(JumpTable(table_label, node.children[1:])) backend.REQUIRES.add('ongoto.asm') def visit_ON_GOSUB(self, node): table_label = backend.tmp_label() - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), '#' + table_label) + self.ic_param(gl.PTR_TYPE, '#' + table_label) yield node.children[0] - self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) - self.emit('call', '__ON_GOSUB', 0) + self.ic_fparam(node.children[0].type_, node.children[0].t) + self.ic_call('__ON_GOSUB', 0) self.JUMP_TABLES.append(JumpTable(table_label, node.children[1:])) backend.REQUIRES.add('ongoto.asm') def visit_CHKBREAK(self, node): if self.PREV_TOKEN != node.token: - self.emit('inline', 'push hl', node.children[0].t) - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.children[0].t) - self.emit('call', 'CHECK_BREAK', 0) + self.ic_inline('push hl', node.children[0].t) + self.ic_fparam(gl.PTR_TYPE, node.children[0].t) + self.ic_call('CHECK_BREAK', 0) backend.REQUIRES.add('break.asm') def visit_IF(self, node): @@ -885,28 +604,27 @@ def visit_IF(self, node): if_label_endif = backend.tmp_label() if len(node.children) == 3: # Has else? - self.emit('jzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, if_label_else) + self.ic_jzero(node.children[0].type_, node.children[0].t, if_label_else) else: - self.emit('jzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, if_label_endif) + self.ic_jzero(node.children[0].type_, node.children[0].t, if_label_endif) yield node.children[1] # THEN... if len(node.children) == 3: # Has else? - self.emit('jump', if_label_endif) - self.emit('label', if_label_else) + self.ic_jump(if_label_endif) + self.ic_label(if_label_else) yield node.children[2] - self.emit('label', if_label_endif) + self.ic_label(if_label_endif) def visit_RETURN(self, node): if len(node.children) == 2: # Something to return? yield node.children[1] - self.emit('ret' + self.TSUFFIX(node.children[1].type_), node.children[1].t, - '%s__leave' % node.children[0].mangled) + self.ic_ret(node.children[1].type_, node.children[1].t, '%s__leave' % node.children[0].mangled) elif len(node.children) == 1: - self.emit('ret', '%s__leave' % node.children[0].mangled) + self.ic_return('%s__leave' % node.children[0].mangled) else: - self.emit('leave', '__fastcall__') + self.ic_leave('__fastcall__') def visit_UNTIL_DO(self, node): loop_label = backend.tmp_label() @@ -914,18 +632,18 @@ def visit_UNTIL_DO(self, node): continue_loop = backend.tmp_label() if node.token == 'UNTIL_DO': - self.emit('jump', continue_loop) + self.ic_jump(continue_loop) - self.emit('label', loop_label) + self.ic_label(loop_label) self.LOOPS.append(('DO', end_loop, continue_loop)) # Saves which labels to jump upon EXIT or CONTINUE if len(node.children) > 1: yield node.children[1] - self.emit('label', continue_loop) + self.ic_label(continue_loop) yield node.children[0] # Condition - self.emit('jzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, loop_label) - self.emit('label', end_loop) + self.ic_jzero(node.children[0].type_, node.children[0].t, loop_label) + self.ic_label(end_loop) self.LOOPS.pop() # del loop_label, end_loop, continue_loop @@ -934,15 +652,15 @@ def visit_WHILE(self, node): end_loop = backend.tmp_label() self.LOOPS.append(('WHILE', end_loop, loop_label)) # Saves which labels to jump upon EXIT or CONTINUE - self.emit('label', loop_label) + self.ic_label(loop_label) yield node.children[0] - self.emit('jzero' + self.TSUFFIX(node.children[0].type_), node.children[0].t, end_loop) + self.ic_jzero(node.children[0].type_, node.children[0].t, end_loop) if len(node.children) > 1: yield node.children[1] - self.emit('jump', loop_label) - self.emit('label', end_loop) + self.ic_jump(loop_label) + self.ic_label(end_loop) self.LOOPS.pop() def visit_WHILE_DO(self, node): @@ -958,10 +676,10 @@ def visit_PLOT(self, node): TMP_HAS_ATTR = self.check_attr(node, 2) yield TMP_HAS_ATTR yield node.children[0] - self.emit('param' + self.TSUFFIX(node.children[0].type_), node.children[0].t) + self.ic_param(node.children[0].type_, node.children[0].t) yield node.children[1] - self.emit('fparam' + self.TSUFFIX(node.children[1].type_), node.children[1].t) - self.emit('call', 'PLOT', 0) # Procedure call. Discard return + self.ic_fparam(node.children[1].type_, node.children[1].t) + self.ic_call('PLOT', 0) backend.REQUIRES.add('plot.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) @@ -969,10 +687,10 @@ def visit_DRAW(self, node): TMP_HAS_ATTR = self.check_attr(node, 2) yield TMP_HAS_ATTR yield node.children[0] - self.emit('param' + self.TSUFFIX(node.children[0].type_), node.children[0].t) + self.ic_param(node.children[0].type_, node.children[0].t) yield node.children[1] - self.emit('fparam' + self.TSUFFIX(node.children[1].type_), node.children[1].t) - self.emit('call', 'DRAW', 0) # Procedure call. Discard return + self.ic_fparam(node.children[1].type_, node.children[1].t) + self.ic_call('DRAW', 0) backend.REQUIRES.add('draw.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) @@ -980,12 +698,12 @@ def visit_DRAW3(self, node): TMP_HAS_ATTR = self.check_attr(node, 3) yield TMP_HAS_ATTR yield node.children[0] - self.emit('param' + self.TSUFFIX(node.children[0].type_), node.children[0].t) + self.ic_param(node.children[0].type_, node.children[0].t) yield node.children[1] - self.emit('param' + self.TSUFFIX(node.children[1].type_), node.children[1].t) + self.ic_param(node.children[1].type_, node.children[1].t) yield node.children[2] - self.emit('fparam' + self.TSUFFIX(node.children[2].type_), node.children[2].t) - self.emit('call', 'DRAW3', 0) # Procedure call. Discard return + self.ic_fparam(node.children[2].type_, node.children[2].t) + self.ic_call('DRAW3', 0) backend.REQUIRES.add('draw3.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) @@ -993,12 +711,12 @@ def visit_CIRCLE(self, node): TMP_HAS_ATTR = self.check_attr(node, 3) yield TMP_HAS_ATTR yield node.children[0] - self.emit('param' + self.TSUFFIX(node.children[0].type_), node.children[0].t) + self.ic_param(node.children[0].type_, node.children[0].t) yield node.children[1] - self.emit('param' + self.TSUFFIX(node.children[1].type_), node.children[1].t) + self.ic_param(node.children[1].type_, node.children[1].t) yield node.children[2] - self.emit('fparam' + self.TSUFFIX(node.children[2].type_), node.children[2].t) - self.emit('call', 'CIRCLE', 0) # Procedure call. Discard return + self.ic_fparam(node.children[2].type_, node.children[2].t) + self.ic_call('CIRCLE', 0) backend.REQUIRES.add('circle.asm') self.HAS_ATTR = (TMP_HAS_ATTR is not None) @@ -1011,7 +729,7 @@ def visit_CIRCLE(self, node): def visit_OUT(self, node): yield node.children[0] yield node.children[1] - self.emit('out', node.children[0].t, node.children[1].t) + self.ic_out(node.children[0].t, node.children[1].t) def visit_PRINT(self, node): for i in node.children: @@ -1020,8 +738,8 @@ def visit_PRINT(self, node): # Print subcommands (AT, OVER, INK, etc... must be skipped here) if i.token in ('PRINT_TAB', 'PRINT_AT', 'PRINT_COMMA',) + self.ATTR_TMP: continue - self.emit('fparam' + self.TSUFFIX(i.type_), i.t) - self.emit('call', '__PRINT' + self.TSUFFIX(i.type_).upper(), 0) + self.ic_fparam(i.type_, i.t) + self.ic_call('__PRINT' + self.TSUFFIX(i.type_).upper(), 0) backend.REQUIRES.add('print' + self.TSUFFIX(i.type_).lower() + '.asm') for i in node.children: @@ -1031,53 +749,53 @@ def visit_PRINT(self, node): if node.eol: if self.HAS_ATTR: - self.emit('call', 'PRINT_EOL_ATTR', 0) + self.ic_call('PRINT_EOL_ATTR', 0) backend.REQUIRES.add('print_eol_attr.asm') self.HAS_ATTR = False else: - self.emit('call', 'PRINT_EOL', 0) + self.ic_call('PRINT_EOL', 0) backend.REQUIRES.add('print.asm') else: self.norm_attr() def visit_PRINT_AT(self, node): yield node.children[0] - self.emit('paramu8', node.children[0].t) + self.ic_param(TYPE.ubyte, node.children[0].t) yield node.children[1] - self.emit('fparamu8', node.children[1].t) - self.emit('call', 'PRINT_AT', 0) # Procedure call. Discard return + self.ic_fparam(TYPE.ubyte, node.children[1].t) + self.ic_call('PRINT_AT', 0) # Procedure call. Discard return backend.REQUIRES.add('print.asm') def visit_PRINT_TAB(self, node): yield node.children[0] - self.emit('fparamu8', node.children[0].t) - self.emit('call', 'PRINT_TAB', 0) + self.ic_fparam(TYPE.ubyte, node.children[0].t) + self.ic_call('PRINT_TAB', 0) backend.REQUIRES.add('print.asm') def visit_PRINT_COMMA(self, node): - self.emit('call', 'PRINT_COMMA', 0) + self.ic_call('PRINT_COMMA', 0) backend.REQUIRES.add('print.asm') def visit_LOAD(self, node): yield node.children[0] - self.emit('paramstr', node.children[0].t) + self.ic_param(TYPE.string, node.children[0].t) yield node.children[1] - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[1].t) + self.ic_param(gl.PTR_TYPE, node.children[1].t) yield node.children[2] - self.emit('param' + self.TSUFFIX(gl.PTR_TYPE), node.children[2].t) + self.ic_param(gl.PTR_TYPE, node.children[2].t) - self.emit('paramu8', int(node.token == 'LOAD')) - self.emit('call', 'LOAD_CODE', 0) + self.ic_param(TYPE.ubyte, int(node.token == 'LOAD')) + self.ic_call('LOAD_CODE', 0) backend.REQUIRES.add('load.asm') def visit_SAVE(self, node): yield (node.children[0]) - self.emit('paramstr', node.children[0].t) + self.ic_param(TYPE.string, node.children[0].t) yield (node.children[1]) - self.emit('paramu16', node.children[1].t) - yield (node.children[2]) - self.emit('paramu16', node.children[2].t) - self.emit('call', 'SAVE_CODE', 0) + self.ic_param(gl.PTR_TYPE, node.children[1].t) + yield node.children[2] + self.ic_param(gl.PTR_TYPE, node.children[2].t) + self.ic_call('SAVE_CODE', 0) backend.REQUIRES.add('save.asm') def visit_VERIFY(self, node): @@ -1085,29 +803,29 @@ def visit_VERIFY(self, node): def visit_BORDER(self, node): yield node.children[0] - self.emit('fparamu8', node.children[0].t) - self.emit('call', 'BORDER', 0) # Procedure call. Discard return + self.ic_fparam(TYPE.ubyte, node.children[0].t) + self.ic_call('BORDER', 0) backend.REQUIRES.add('border.asm') def visit_BEEP(self, node): if node.children[0].token == node.children[1].token == 'NUMBER': # BEEP , DE, HL = arch.zx48k.beep.getDEHL(float(node.children[0].t), float(node.children[1].t)) - self.emit('paramu16', HL) - self.emit('fparamu16', DE) - self.emit('call', '__BEEPER', 0) # Procedure call. Discard return + self.ic_param(TYPE.uinteger, HL) + self.ic_fparam(TYPE.uinteger, DE) + self.ic_call('__BEEPER', 0) # Procedure call. Discard return backend.REQUIRES.add('beeper.asm') else: yield node.children[1] - self.emit('paramf', node.children[1].t) + self.ic_param(TYPE.float_, node.children[1].t) yield node.children[0] - self.emit('fparamf', node.children[0].t) - self.emit('call', 'BEEP', 0) # Procedure call. Discard return + self.ic_fparam(TYPE.float_, node.children[0].t) + self.ic_call('BEEP', 0) backend.REQUIRES.add('beep.asm') def visit_PAUSE(self, node): yield node.children[0] - self.emit('fparam' + self.TSUFFIX(node.children[0].type_), node.children[0].t) - self.emit('call', '__PAUSE', 0) + self.ic_fparam(node.children[0].type_, node.children[0].t) + self.ic_call('__PAUSE', 0) backend.REQUIRES.add('pause.asm') # endregion @@ -1118,8 +836,8 @@ def visit_PAUSE(self, node): # ----------------------------------------------------------------------- def visit_ATTR_sentence(self, node): yield node.children[0] - self.emit('fparamu8', node.children[0].t) - self.emit('call', node.token, 0) + self.ic_fparam(TYPE.ubyte, node.children[0].t) + self.ic_call(node.token, 0) backend.REQUIRES.add('%s.asm' % node.token.lower()) self.HAS_ATTR = True @@ -1154,7 +872,7 @@ def visit_ITALIC(self, node): # Other Sentences, like ASM, etc.. # ----------------------------------------------------------------------------------------------------- def visit_ASM(self, node): - self.emit('inline', node.asm, node.lineno) + self.ic_inline(node.asm, node.lineno) # endregion @@ -1175,13 +893,13 @@ def emit_var_assign(self, var, t): raise NotImplementedError() if var.scope == SCOPE.global_: - self.emit('store' + self.TSUFFIX(var.type_), var.mangled, t) + self.ic_store(var.type_, var.mangled, t) elif var.scope == SCOPE.parameter: - self.emit('pstore' + self.TSUFFIX(var.type_), p + str(var.offset), t) + self.ic_pstore(var.type_, p + str(var.offset), t) elif var.scope == SCOPE.local: if var.alias is not None and var.alias.class_ == CLASS.array: var.offset -= 1 + 2 * var.alias.count - self.emit('pstore' + self.TSUFFIX(var.type_), p + str(-var.offset), t) + self.ic_pstore(var.type_, p + str(-var.offset), t) def emit_let_left_part(self, node, t=None): var = node.children[0] @@ -1329,9 +1047,9 @@ class VarTranslator(TranslatorVisitor): """ def visit_LABEL(self, node): - self.emit('label', node.mangled) + self.ic_label(node.mangled) for tmp in node.aliased_by: - self.emit('label', tmp.mangled) + self.ic_label(tmp.mangled) def visit_VARDECL(self, node): entry = node.entry @@ -1341,20 +1059,19 @@ def visit_VARDECL(self, node): return if entry.addr is not None: - self.emit('deflabel', entry.mangled, entry.addr) + self.ic_deflabel(entry.mangled, entry.addr) for entry in entry.aliased_by: - self.emit('deflabel', entry.mangled, entry.addr) + self.ic_deflabel(entry.mangled, entry.addr) elif entry.alias is None: for alias in entry.aliased_by: - self.emit('label', alias.mangled) + self.ic_label(alias.mangled) if entry.default_value is None: - self.emit('var', entry.mangled, entry.size) + self.ic_var(entry.mangled, entry.size) else: if isinstance(entry.default_value, symbols.CONST) and entry.default_value.token == 'CONST': - self.emit('varx', node.mangled, self.TSUFFIX(node.type_), - [self.traverse_const(entry.default_value)]) + self.ic_varx(node.mangled, node.type_, [self.traverse_const(entry.default_value)]) else: - self.emit('vard', node.mangled, Translator.default_value(node.type_, entry.default_value)) + self.ic_vard(node.mangled, Translator.default_value(node.type_, entry.default_value)) def visit_ARRAYDECL(self, node): entry = node.entry @@ -1376,7 +1093,7 @@ def visit_ARRAYDECL(self, node): arr_data = [] if entry.addr: - self.emit('deflabel', data_label, "%s" % entry.addr) + self.ic_deflabel(data_label, "%s" % entry.addr) else: if entry.default_value is not None: arr_data = Translator.array_default_value(node.type_, entry.default_value) @@ -1384,28 +1101,28 @@ def visit_ARRAYDECL(self, node): arr_data = ['00'] * node.size for alias in entry.aliased_by: - offset = 1 + 2 * entry.count + alias.offset # TODO: Generalize for multi-arch - self.emit('deflabel', alias.mangled, '%s + %i' % (entry.mangled, offset)) + offset = 1 + 2 * TYPE.size(gl.PTR_TYPE) + alias.offset # TODO: Generalize for multi-arch + self.ic_deflabel(alias.mangled, '%s + %i' % (entry.mangled, offset)) - self.emit('varx', node.mangled, self.TSUFFIX(gl.PTR_TYPE), [idx_table_label]) + self.ic_varx(node.mangled, gl.PTR_TYPE, [idx_table_label]) if entry.addr: - self.emit('varx', entry.data_ptr_label, self.TSUFFIX(gl.PTR_TYPE), [self.traverse_const(entry.addr)]) + self.ic_varx(entry.data_ptr_label, gl.PTR_TYPE, [self.traverse_const(entry.addr)]) else: - self.emit('varx', entry.data_ptr_label, self.TSUFFIX(gl.PTR_TYPE), [data_label]) - self.emit('vard', data_label, arr_data) + self.ic_varx(entry.data_ptr_label, gl.PTR_TYPE, [data_label]) + self.ic_vard(data_label, arr_data) - self.emit('vard', idx_table_label, l) + self.ic_vard(idx_table_label, l) if entry.lbound_used: l = ['%04X' % len(node.bounds)] + \ ['%04X' % bound.lower for bound in node.bounds] - self.emit('vard', '__LBOUND__.' + entry.mangled, l) + self.ic_vard('__LBOUND__.' + entry.mangled, l) if entry.ubound_used: l = ['%04X' % len(node.bounds)] + \ ['%04X' % bound.upper for bound in node.bounds] - self.emit('vard', '__UBOUND__.' + entry.mangled, l) + self.ic_vard('__UBOUND__.' + entry.mangled, l) class UnaryOpTranslator(TranslatorVisitor): @@ -1414,15 +1131,15 @@ class UnaryOpTranslator(TranslatorVisitor): def visit_MINUS(self, node): yield node.operand - self.emit('neg' + self.TSUFFIX(node.type_), node.t, node.operand.t) + self.ic_neg(node.type_, node.t, node.operand.t) def visit_NOT(self, node): yield node.operand - self.emit('not' + self.TSUFFIX(node.operand.type_), node.t, node.operand.t) + self.ic_not(node.operand.type_, node.t, node.operand.t) def visit_BNOT(self, node): yield node.operand - self.emit('bnot' + self.TSUFFIX(node.operand.type_), node.t, node.operand.t) + self.ic_bnot(node.operand.type_, node.t, node.operand.t) def visit_ADDRESS(self, node): scope = node.children[0].scope @@ -1430,18 +1147,18 @@ def visit_ADDRESS(self, node): yield node.children[0] # Address of an array element. if scope == SCOPE.global_: - self.emit('aaddr', node.t, node.children[0].entry.mangled) - elif scope == 'parameter': - self.emit('paaddr', node.t, node.children[0].entry.offset) - elif scope == 'local': - self.emit('paaddr', node.t, -node.children[0].entry.offset) + self.ic_aaddr(node.t, node.children[0].entry.mangled) + elif scope == SCOPE.parameter: + self.ic_paaddr(node.t, node.children[0].entry.offset) + elif scope == SCOPE.local: + self.ic_paaddr(node.t, -node.children[0].entry.offset) else: # It's a scalar variable if scope == SCOPE.global_: - self.emit('load' + self.TSUFFIX(node.type_), node.t, '#' + node.operand.t) + self.ic_load(node.type_, node.t, '#' + node.operand.t) elif scope == SCOPE.parameter: - self.emit('paddr', node.operand.offset + node.operand.type_.size % 2, node.t) + self.ic_paddr(node.operand.offset + node.operand.type_.size % 2, node.t) elif scope == SCOPE.local: - self.emit('paddr', -node.operand.offset, node.t) + self.ic_paddr(-node.operand.offset, node.t) class BuiltinTranslator(TranslatorVisitor): @@ -1451,138 +1168,138 @@ class BuiltinTranslator(TranslatorVisitor): # region STRING Functions def visit_INKEY(self, node): - self.emit('call', 'INKEY', Type.string.size) + self.ic_call('INKEY', Type.string.size) backend.REQUIRES.add('inkey.asm') def visit_IN(self, node): - self.emit('in', node.children[0].t) + self.ic_in(node.children[0].t) def visit_CODE(self, node): - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.operand.t) + self.ic_fparam(gl.PTR_TYPE, node.operand.t) if node.operand.token != 'STRING' and node.operand.token != 'VAR' and node.operand.t != '_': - self.emit('fparamu8', 1) # If the argument is not a variable, it must be freed + self.ic_fparam(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed else: - self.emit('fparamu8', 0) + self.ic_fparam(TYPE.ubyte, 0) - self.emit('call', '__ASC', Type.ubyte.size) # Expect a char code + self.ic_call('__ASC', Type.ubyte.size) # Expect a char code backend.REQUIRES.add('asc.asm') def visit_CHR(self, node): - self.emit('fparam' + self.TSUFFIX(gl.STR_INDEX_TYPE), len(node.operand)) # Number of args - self.emit('call', 'CHR', node.size) + self.ic_fparam(gl.STR_INDEX_TYPE, len(node.operand)) # Number of args + self.ic_call('CHR', node.size) backend.REQUIRES.add('chr.asm') def visit_STR(self, node): - self.emit('fparamf', node.children[0].t) - self.emit('call', '__STR_FAST', node.type_.size) + self.ic_fparam(TYPE.float_, node.children[0].t) + self.ic_call('__STR_FAST', node.type_.size) backend.REQUIRES.add('str.asm') def visit_LEN(self, node): - self.emit('lenstr', node.t, node.operand.t) + self.ic_lenstr(node.t, node.operand.t) def visit_VAL(self, node): - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.operand.t) + self.ic_fparam(gl.PTR_TYPE, node.operand.t) if node.operand.token not in ('STRING', 'VAR') and node.operand.t != '_': - self.emit('fparamu8', 1) # If the argument is not a variable, it must be freed + self.ic_fparam(TYPE.ubyte, 1) # If the argument is not a variable, it must be freed else: - self.emit('fparamu8', 0) + self.ic_fparam(TYPE.ubyte, 0) - self.emit('call', 'VAL', node.type_.size) + self.ic_call('VAL', node.type_.size) backend.REQUIRES.add('val.asm') + # endregion + def visit_ABS(self, node): - self.emit('abs' + self.TSUFFIX(node.children[0].type_), node.t, node.children[0].t) + self.ic_abs(node.children[0].type_, node.t, node.children[0].t) def visit_RND(self, node): # A special "ZEROARY" function with no parameters - self.emit('call', 'RND', Type.float_.size) + self.ic_call('RND', Type.float_.size) backend.REQUIRES.add('random.asm') - # endregion - def visit_PEEK(self, node): - self.emit('load' + self.TSUFFIX(node.type_), node.t, '*' + str(node.children[0].t)) + self.ic_load(node.type_, node.t, '*' + str(node.children[0].t)) # region MATH Functions def visit_SIN(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'SIN', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('SIN', node.size) self.REQUIRES.add('sin.asm') def visit_COS(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'COS', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('COS', node.size) self.REQUIRES.add('cos.asm') def visit_TAN(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'TAN', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('TAN', node.size) self.REQUIRES.add('tan.asm') def visit_ASN(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'ASIN', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('ASIN', node.size) self.REQUIRES.add('asin.asm') def visit_ACS(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'ACOS', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('ACOS', node.size) self.REQUIRES.add('acos.asm') def visit_ATN(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'ATAN', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('ATAN', node.size) self.REQUIRES.add('atan.asm') def visit_EXP(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'EXP', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('EXP', node.size) self.REQUIRES.add('exp.asm') def visit_LN(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'LN', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('LN', node.size) self.REQUIRES.add('logn.asm') def visit_SGN(self, node): s = self.TSUFFIX(node.operand.type_) - self.emit('fparam' + s, node.operand.t) - self.emit('call', '__SGN%s' % s.upper(), node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('__SGN%s' % s.upper(), node.size) self.REQUIRES.add('sgn%s.asm' % s) def visit_SQR(self, node): - self.emit('fparam' + self.TSUFFIX(node.operand.type_), node.operand.t) - self.emit('call', 'SQRT', node.size) + self.ic_fparam(node.operand.type_, node.operand.t) + self.ic_call('SQRT', node.size) self.REQUIRES.add('sqrt.asm') # endregion def visit_LBOUND(self, node): entry = node.operands[0] - self.emit('param' + self.TSUFFIX(gl.BOUND_TYPE), '#__LBOUND__.' + entry.mangled) + self.ic_param(gl.BOUND_TYPE, '#__LBOUND__.' + entry.mangled) yield node.operands[1] - self.emit('fparam' + self.TSUFFIX(gl.BOUND_TYPE), optemps.new_t()) - self.emit('call', '__BOUND', self.TYPE(gl.BOUND_TYPE).size) + self.ic_fparam(gl.BOUND_TYPE, optemps.new_t()) + self.ic_call('__BOUND', self.TYPE(gl.BOUND_TYPE).size) backend.REQUIRES.add('bound.asm') def visit_UBOUND(self, node): entry = node.operands[0] - self.emit('param' + self.TSUFFIX(gl.BOUND_TYPE), '#__UBOUND__.' + entry.mangled) + self.ic_param(gl.BOUND_TYPE, '#__UBOUND__.' + entry.mangled) yield node.operands[1] - self.emit('fparam' + self.TSUFFIX(gl.BOUND_TYPE), optemps.new_t()) - self.emit('call', '__BOUND', self.TYPE(gl.BOUND_TYPE).size) + self.ic_fparam(gl.BOUND_TYPE, optemps.new_t()) + self.ic_call('__BOUND', self.TYPE(gl.BOUND_TYPE).size) backend.REQUIRES.add('bound.asm') def visit_USR_STR(self, node): # USR ADDR - self.emit('fparamstr', node.children[0].t) - self.emit('call', 'USR_STR', node.type_.size) + self.ic_fparam(TYPE.string, node.children[0].t) + self.ic_call('USR_STR', node.type_.size) backend.REQUIRES.add('usr_str.asm') def visit_USR(self, node): """ Machine code call from basic """ - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), node.children[0].t) - self.emit('call', 'USR', node.type_.size) + self.ic_fparam(gl.PTR_TYPE, node.children[0].t) + self.ic_call('USR', node.type_.size) backend.REQUIRES.add('usr.asm') @@ -1599,6 +1316,14 @@ def __init__(self, function_list): assert isinstance(x, symbols.FUNCTION) self.functions = function_list + def _local_array_load(self, scope, local_var): + t2 = optemps.new_t() + if scope == SCOPE.parameter: + self.ic_pload(gl.PTR_TYPE, t2, '%i' % (local_var.offset - self.TYPE(gl.PTR_TYPE).size)) + elif scope == SCOPE.local: + self.ic_pload(gl.PTR_TYPE, t2, '%i' % -(local_var.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_fparam(gl.PTR_TYPE, t2) + def start(self): while self.functions: f = self.functions.pop(0) @@ -1606,11 +1331,11 @@ def start(self): self.visit(f) def visit_FUNCTION(self, node): - self.emit('label', node.mangled) + self.ic_label(node.mangled) if node.convention == CONVENTION.fastcall: - self.emit('enter', '__fastcall__') + self.ic_enter('__fastcall__') else: - self.emit('enter', node.locals_size) + self.ic_enter(node.locals_size) for local_var in node.local_symbol_table.values(): if not local_var.accessed: # HINT: This should never happens as values() is already filtered @@ -1630,23 +1355,22 @@ def visit_FUNCTION(self, node): r = [] if local_var.default_value is not None: r.extend(self.array_default_value(local_var.type_, local_var.default_value)) - self.emit('larrd', local_var.offset, q, local_var.size, r) # Initializes array bounds + self.ic_larrd(local_var.offset, q, local_var.size, r) # Initializes array bounds elif local_var.class_ == CLASS.const: continue else: # Local vars always defaults to 0, so if 0 we do nothing if local_var.default_value is not None and local_var.default_value != 0: if isinstance(local_var.default_value, symbols.CONST) and \ local_var.default_value.token == 'CONST': - self.emit('lvarx', local_var.offset, self.TSUFFIX(local_var.type_), - [self.traverse_const(local_var.default_value)]) + self.ic_lvarx(local_var.type_, local_var.offset, [self.traverse_const(local_var.default_value)]) else: q = self.default_value(local_var.type_, local_var.default_value) - self.emit('lvard', local_var.offset, q) + self.ic_lvard(local_var.offset, q) for i in node.body: yield i - self.emit('label', '%s__leave' % node.mangled) + self.ic_label('%s__leave' % node.mangled) # Now free any local string from memory. preserve_hl = False @@ -1657,11 +1381,11 @@ def visit_FUNCTION(self, node): if scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref): if not preserve_hl: preserve_hl = True - self.emit('exchg') + self.ic_exchg() offset = -local_var.offset if scope == SCOPE.local else local_var.offset - self.emit('fploadstr', local_var.t, offset) - self.emit('call', '__MEM_FREE', 0) + self.ic_fpload(TYPE.string, local_var.t, offset) + self.ic_call('__MEM_FREE', 0) self.REQUIRES.add('free.asm') elif local_var.class_ == CLASS.const: continue @@ -1669,45 +1393,30 @@ def visit_FUNCTION(self, node): if scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref): if not preserve_hl: preserve_hl = True - self.emit('exchg') - - self.emit('param' + self.TSUFFIX(gl.BOUND_TYPE), local_var.count) - t2 = optemps.new_t() - if scope == SCOPE.parameter: - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t2, - '%i' % (local_var.offset - self.TYPE(gl.PTR_TYPE).size)) - elif scope == SCOPE.local: - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t2, - '%i' % -(local_var.offset - self.TYPE(gl.PTR_TYPE).size)) - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), t2) - self.emit('call', '__ARRAYSTR_FREE_MEM', 0) # frees all the strings and the array itself + self.ic_exchg() + + self.ic_param(gl.BOUND_TYPE, local_var.count) + self._local_array_load(scope, local_var) + self.ic_call('__ARRAYSTR_FREE_MEM', 0) self.REQUIRES.add('arraystrfree.asm') if local_var.class_ == CLASS.array and local_var.type_ != self.TYPE(TYPE.string) and \ (scope == SCOPE.local or (scope == SCOPE.parameter and not local_var.byref)): if not preserve_hl: preserve_hl = True - self.emit('exchg') - - t2 = optemps.new_t() - if scope == SCOPE.parameter: - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t2, '%i' - % (local_var.offset - self.TYPE(gl.PTR_TYPE).size)) - elif scope == SCOPE.local: - self.emit('pload%s' % self.TSUFFIX(gl.PTR_TYPE), t2, '%i' - % -(local_var.offset - self.TYPE(gl.PTR_TYPE).size)) + self.ic_exchg() - self.emit('fparam' + self.TSUFFIX(gl.PTR_TYPE), t2) - self.emit('call', '__MEM_FREE', 0) + self._local_array_load(scope, local_var) + self.ic_call('__MEM_FREE', 0) self.REQUIRES.add('free.asm') if preserve_hl: - self.emit('exchg') + self.ic_exchg() if node.convention == CONVENTION.fastcall: - self.emit('leave', CONVENTION.to_string(node.convention)) + self.ic_leave(CONVENTION.to_string(node.convention)) else: - self.emit('leave', node.params.size) + self.ic_leave(node.params.size) def visit_FUNCDECL(self, node): """ Nested scope functions diff --git a/arch/zx48k/translatorinstvisitor.py b/arch/zx48k/translatorinstvisitor.py new file mode 100644 index 000000000..911a5d599 --- /dev/null +++ b/arch/zx48k/translatorinstvisitor.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 -*- + +from api.constants import TYPE +import symbols + +from ast_ import NodeVisitor +from .backend import Quad, MEMORY +from api.debug import __DEBUG__ + + +class TranslatorInstVisitor(NodeVisitor): + @staticmethod + def emit(*args): + """ Convert the given args to a Quad (3 address code) instruction + """ + quad = Quad(*args) + __DEBUG__('EMIT ' + str(quad)) + MEMORY.append(quad) + + @staticmethod + def TSUFFIX(type_): + assert isinstance(type_, symbols.TYPE) or TYPE.is_valid(type_) + + _TSUFFIX = {TYPE.byte_: 'i8', TYPE.ubyte: 'u8', + TYPE.integer: 'i16', TYPE.uinteger: 'u16', + TYPE.long_: 'i32', TYPE.ulong: 'u32', + TYPE.fixed: 'f16', TYPE.float_: 'f', + TYPE.string: 'str' + } + + if isinstance(type_, symbols.TYPEREF): + type_ = type_.final + assert isinstance(type_, symbols.BASICTYPE) + + if isinstance(type_, symbols.BASICTYPE): + return _TSUFFIX[type_.type_] + + return _TSUFFIX[type_] + + def ic_aaddr(self, t1, t2): + return self.emit('aaddr', t1, t2) + + def ic_abs(self, type_, t1, t2): + return self.emit('abs' + self.TSUFFIX(type_), t1, t2) + + def ic_add(self, type_, t1, t2, t3): + return self.emit('add' + self.TSUFFIX(type_), t1, t2, t3) + + def ic_aload(self, type_, t1, mangle: str): + return self.emit('aload' + self.TSUFFIX(type_), t1, mangle) + + def ic_and(self, type_): + return self.emit('and' + self.TSUFFIX(type_)) + + def ic_astore(self, type_, addr: str, t): + return self.emit('astore' + self.TSUFFIX(type_), addr, t) + + def ic_band(self, type_): + return self.emit('band' + self.TSUFFIX(type_)) + + def ic_bnot(self, type_, t1, t2): + return self.emit('bnot' + self.TSUFFIX(type_), t1, t2) + + def ic_bor(self, type_): + return self.emit('bor' + self.TSUFFIX(type_)) + + def ic_bxor(self, type_): + return self.emit('bxor' + self.TSUFFIX(type_)) + + def ic_call(self, label: str, num: int): + return self.emit('call', label, num) + + def ic_cast(self, t1, type1, type2, t2): + self.emit('cast', t1, self.TSUFFIX(type1), self.TSUFFIX(type2), t2) + + def ic_data(self, type_, data: list): + return self.emit('data', self.TSUFFIX(type_), data) + + def ic_deflabel(self, label: str, t): + return self.emit('deflabel', label, t) + + def ic_div(self, type_): + return self.emit('div' + self.TSUFFIX(type_)) + + def ic_end(self, t): + return self.emit('end', t) + + def ic_enter(self, arg): + return self.emit('enter', arg) + + def ic_eq(self, type_, t, t1, t2): + return self.emit('eq' + self.TSUFFIX(type_), t, t1, t2) + + def ic_exchg(self): + return self.emit('exchg') + + def ic_fparam(self, type_, t): + return self.emit('fparam' + self.TSUFFIX(type_), t) + + def ic_fpload(self, type_, t, offset): + return self.emit('fpload' + self.TSUFFIX(type_), t, offset) + + def ic_ge(self, type_, t, t1, t2): + return self.emit('ge' + self.TSUFFIX(type_), t, t1, t2) + + def ic_gt(self, type_, t, t1, t2): + return self.emit('gt' + self.TSUFFIX(type_), t, t1, t2) + + def ic_in(self, t): + return self.emit('in', t) + + def ic_inline(self, asm_code: str, t): + return self.emit('inline', asm_code, t) + + def ic_jgezero(self, type_, t, label: str): + return self.emit('jgezero' + self.TSUFFIX(type_), t, label) + + def ic_jnzero(self, type_, t, label: str): + return self.emit('jnzero' + self.TSUFFIX(type_), t, label) + + def ic_jump(self, label: str): + return self.emit('jump', label) + + def ic_jzero(self, type_, t, label: str): + return self.emit('jzero' + self.TSUFFIX(type_), t, label) + + def ic_label(self, label: str): + return self.emit('label', label) + + def ic_larrd(self, offset, arg1, size, arg2): + return self.emit('larrd', offset, arg1, size, arg2) + + def ic_le(self, type_, t, t1, t2): + return self.emit('le' + self.TSUFFIX(type_), t, t1, t2) + + def ic_leave(self, convention: str): + return self.emit('leave', convention) + + def ic_lenstr(self, t1, t2): + return self.emit('lenstr', t1, t2) + + def ic_load(self, type_, t1, t2): + return self.emit('load' + self.TSUFFIX(type_), t1, t2) + + def ic_lt(self, type_, t, t1, t2): + return self.emit('lt' + self.TSUFFIX(type_), t, t1, t2) + + def ic_lvard(self, offset, default_value: list): + return self.emit('lvard', offset, default_value) + + def ic_lvarx(self, type_, offset, default_value: list): + self.emit('lvarx', offset, self.TSUFFIX(type_), default_value) + + def ic_memcopy(self, t1, t2, t3): + return self.emit('memcopy', t1, t2, t3) + + def ic_mod(self, type_): + return self.emit('mod' + self.TSUFFIX(type_)) + + def ic_mul(self, type_): + return self.emit('mul' + self.TSUFFIX(type_)) + + def ic_ne(self, type_, t, t1, t2): + return self.emit('ne' + self.TSUFFIX(type_), t, t1, t2) + + def ic_neg(self, type_, t1, t2): + return self.emit('neg' + self.TSUFFIX(type_), t1, t2) + + def ic_nop(self): + return self.emit('nop') + + def ic_not(self, type_, t1, t2): + return self.emit('not' + self.TSUFFIX(type_), t1, t2) + + def ic_or(self, type_): + return self.emit('or' + self.TSUFFIX(type_)) + + def ic_org(self, type_): + return self.emit('org' + self.TSUFFIX(type_)) + + def ic_out(self, t1, t2): + return self.emit('out', t1, t2) + + def ic_paaddr(self, t1, t2): + return self.emit('paaddr', t1, t2) + + def ic_paddr(self, t1, t2): + return self.emit('paddr', t1, t2) + + def ic_paload(self, type_, t, offset: int): + return self.emit('paload' + self.TSUFFIX(type_), t, offset) + + def ic_param(self, type_, t): + return self.emit('param' + self.TSUFFIX(type_), t) + + def ic_pastore(self, type_, offset, t): + return self.emit('pastore' + self.TSUFFIX(type_), offset, t) + + def ic_pload(self, type_, t1, t2): + return self.emit('pload' + self.TSUFFIX(type_), t1, t2) + + def ic_pow(self, type_): + return self.emit('pow' + self.TSUFFIX(type_)) + + def ic_pstore(self, type_, offset, t): + return self.emit('pstore' + self.TSUFFIX(type_), offset, t) + + def ic_ret(self, type_, t, addr): + return self.emit('ret' + self.TSUFFIX(type_), t, addr) + + def ic_return(self, addr): + return self.emit('ret', addr) + + def ic_shl(self, type_): + return self.emit('shl' + self.TSUFFIX(type_)) + + def ic_shr(self, type_): + return self.emit('shr' + self.TSUFFIX(type_)) + + def ic_store(self, type_, t1, t2): + return self.emit('store' + self.TSUFFIX(type_), t1, t2) + + def ic_sub(self, type_): + return self.emit('sub' + self.TSUFFIX(type_)) + + def ic_var(self, name: str, size_): + return self.emit('var', name, size_) + + def ic_vard(self, name: str, data: list): + return self.emit('vard', name, data) + + def ic_varx(self, name: str, type_, default_value: list): + return self.emit('varx', name, self.TSUFFIX(type_), default_value) + + def ic_xor(self, type_): + return self.emit('xor' + self.TSUFFIX(type_)) diff --git a/arch/zx48k/translatorvisitor.py b/arch/zx48k/translatorvisitor.py new file mode 100644 index 000000000..d3c0f6253 --- /dev/null +++ b/arch/zx48k/translatorvisitor.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- + +from collections import OrderedDict +from api.errmsg import syntax_error_not_constant +from api.errmsg import syntax_error_cant_convert_to_type +from api.debug import __DEBUG__ + +from api.errors import InvalidCONSTexpr +from api.config import OPTIONS +from api.constants import TYPE +from api.constants import SCOPE +import api.global_ as gl + +from symbols.symbol_ import Symbol +from symbols.type_ import Type + +from . import backend +import symbols + +from api.errors import InvalidOperatorError + +from .translatorinstvisitor import TranslatorInstVisitor + + +class TranslatorVisitor(TranslatorInstVisitor): + """ This visitor just adds the emit() method. + """ + # ------------------------------------------------ + # A list of tokens that belongs to temporary + # ATTR setting + # ------------------------------------------------ + ATTR = ('INK', 'PAPER', 'BRIGHT', 'FLASH', 'OVER', 'INVERSE', 'BOLD', 'ITALIC') + ATTR_TMP = tuple(x + '_TMP' for x in ATTR) + + # Local flags + HAS_ATTR = False + + # Previous Token + PREV_TOKEN = None + + # Current Token + CURR_TOKEN = None + + LOOPS = [] # Defined LOOPS + STRING_LABELS = OrderedDict() + JUMP_TABLES = [] + + # Type code used in DATA + DATA_TYPES = { + 'str': 1, + 'i8': 2, + 'u8': 3, + 'i16': 4, + 'u16': 5, + 'i32': 6, + 'u32': 7, + 'f16': 8, + 'f': 9 + } + + @classmethod + def reset(cls): + cls.LOOPS = [] # Defined LOOPS + cls.STRING_LABELS = OrderedDict() + cls.JUMP_TABLES = [] + + def add_string_label(self, str_): + """ Maps ("folds") the given string, returning an unique label ID. + This allows several constant labels to be initialized to the same address + thus saving memory space. + :param str_: the string to map + :return: the unique label ID + """ + if self.STRING_LABELS.get(str_, None) is None: + self.STRING_LABELS[str_] = backend.tmp_label() + + return self.STRING_LABELS[str_] + + @property + def O_LEVEL(self): + return OPTIONS.optimization.value + + @staticmethod + def TYPE(type_): + """ Converts a backend type (from api.constants) + to a SymbolTYPE object (taken from the SYMBOL_TABLE). + If type_ is already a SymbolTYPE object, nothing + is done. + """ + if isinstance(type_, symbols.TYPE): + return type_ + + assert TYPE.is_valid(type_) + return gl.SYMBOL_TABLE.basic_types[type_] + + @staticmethod + def dumpMemory(MEMORY): + """ Returns a sequence of Quads + """ + for x in MEMORY: + yield str(x) + + # Generic Visitor methods + def visit_BLOCK(self, node): + __DEBUG__('BLOCK', 2) + for child in node.children: + yield child + + # Visits any temporal attribute + def visit_ATTR_TMP(self, node): + yield node.children[0] + self.ic_fparam(node.children[0].type_, node.children[0].t) + self.ic_call(node.token, 0) # Procedure call. Discard return + ifile = node.token.lower() + ifile = ifile[:ifile.index('_')] + backend.REQUIRES.add(ifile + '.asm') + + # This function must be called before emit_strings + def emit_data_blocks(self): + if not gl.DATA_IS_USED: + return # nothing to do + + for label_, datas in gl.DATAS: + self.ic_label(label_) + for d in datas: + if isinstance(d, symbols.FUNCDECL): + type_ = '%02Xh' % (self.DATA_TYPES[self.TSUFFIX(d.type_)] | 0x80) + self.ic_data(TYPE.byte_, [type_]) + self.ic_data(gl.PTR_TYPE, [d.mangled]) + continue + + self.ic_data(TYPE.byte_, [self.DATA_TYPES[self.TSUFFIX(d.value.type_)]]) + if d.value.type_ == self.TYPE(TYPE.string): + lbl = self.add_string_label(d.value.value) + self.ic_data(gl.PTR_TYPE, [lbl]) + elif d.value.type_ == self.TYPE(TYPE.fixed): # Convert to bytes + bytes_ = 0xFFFFFFFF & int(d.value.value * 2 ** 16) + self.ic_data(TYPE.uinteger, ['0x%04X' % (bytes_ & 0xFFFF), '0x%04X' % (bytes_ >> 16)]) + else: + self.ic_data(d.value.type_, [self.traverse_const(d.value)]) + + if not gl.DATAS: # The above loop was not executed, because there's no data + self.ic_label('__DATA__0') + + self.ic_vard('__DATA__END', ['00']) + + def emit_strings(self): + for str_, label_ in self.STRING_LABELS.items(): + l = '%04X' % (len(str_) & 0xFFFF) # TODO: Universalize for any arch + self.ic_vard(label_, [l] + ['%02X' % ord(x) for x in str_]) + + def emit_jump_tables(self): + for table_ in self.JUMP_TABLES: + self.ic_vard(table_.label, [str(len(table_.addresses))] + ['##' + x.mangled for x in table_.addresses]) + + def _visit(self, node): + self.norm_attr() + if isinstance(node, Symbol): + __DEBUG__('Visiting {}'.format(node.token), 1) + if node.token in self.ATTR_TMP: + return self.visit_ATTR_TMP(node) + + return TranslatorInstVisitor._visit(self, node) + + def norm_attr(self): + """ Normalize attr state + """ + if not self.HAS_ATTR: + return + + self.HAS_ATTR = False + self.ic_call('COPY_ATTR', 0) + backend.REQUIRES.add('copy_attr.asm') + + @staticmethod + def traverse_const(node): + """ Traverses a constant and returns an string + with the arithmetic expression + """ + if node.token == 'NUMBER': + return node.t + + if node.token == 'UNARY': + mid = node.operator + if mid == 'MINUS': + result = ' -' + TranslatorVisitor.traverse_const(node.operand) + elif mid == 'ADDRESS': + if node.operand.scope == SCOPE.global_ or node.operand.token in ('LABEL', 'FUNCTION'): + result = TranslatorVisitor.traverse_const(node.operand) + else: + syntax_error_not_constant(node.operand.lineno) + return + else: + raise InvalidOperatorError(mid) + return result + + if node.token == 'BINARY': + mid = node.operator + if mid == 'PLUS': + mid = '+' + elif mid == 'MINUS': + mid = '-' + elif mid == 'MUL': + mid = '*' + elif mid == 'DIV': + mid = '/' + elif mid == 'MOD': + mid = '%' + elif mid == 'POW': + mid = '^' + elif mid == 'SHL': + mid = '>>' + elif mid == 'SHR': + mid = '<<' + else: + raise InvalidOperatorError(mid) + + return '(%s) %s (%s)' % (TranslatorVisitor.traverse_const(node.left), mid, + TranslatorVisitor.traverse_const(node.right)) + + if node.token == 'TYPECAST': + if node.type_ in (Type.byte_, Type.ubyte): + return '(' + TranslatorVisitor.traverse_const(node.operand) + ') & 0xFF' + if node.type_ in (Type.integer, Type.uinteger): + return '(' + TranslatorVisitor.traverse_const(node.operand) + ') & 0xFFFF' + if node.type_ in (Type.long_, Type.ulong): + return '(' + TranslatorVisitor.traverse_const(node.operand) + ') & 0xFFFFFFFF' + if node.type_ == Type.fixed: + return '((' + TranslatorVisitor.traverse_const(node.operand) + ') & 0xFFFF) << 16' + syntax_error_cant_convert_to_type(node.lineno, str(node.operand), node.type_) + return + + if node.token == 'VARARRAY': + return node.data_label + + if node.token in ('VAR', 'LABEL', 'FUNCTION'): + # TODO: Check what happens with local vars and params + return node.t + + if node.token == 'CONST': + return TranslatorVisitor.traverse_const(node.expr) + + if node.token == 'ARRAYACCESS': + return '({} + {})'.format(node.entry.data_label, node.offset) + + raise InvalidCONSTexpr(node) + + @staticmethod + def check_attr(node, n): + """ Check if ATTR has to be normalized + after this instruction has been translated + to intermediate code. + """ + if len(node.children) > n: + return node.children[n] diff --git a/tests/functional/opt1_beep_const.asm b/tests/functional/opt1_beep_const.asm new file mode 100644 index 000000000..4783fa8c2 --- /dev/null +++ b/tests/functional/opt1_beep_const.asm @@ -0,0 +1,54 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld hl, 1642 + push hl + ld hl, 261 + call __BEEPER + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "beeper.asm" +; vim:ts=4:et:sw=4: + ; This is a fast beep routine, but needs parameters + ; codified in a different way. +; See http://www.wearmouth.demon.co.uk/zx82.htm#L03F8 + ; Needs pitch on top of the stack + ; HL = duration +__BEEPER: + ex de, hl + pop hl + ex (sp), hl ; CALLEE + push ix ; BEEPER changes IX + call 03B5h + pop ix + ret +#line 22 "opt1_beep_const.bas" +ZXBASIC_USER_DATA: + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt1_beep_const.bas b/tests/functional/opt1_beep_const.bas new file mode 100644 index 000000000..ffcbeb12a --- /dev/null +++ b/tests/functional/opt1_beep_const.bas @@ -0,0 +1,4 @@ +' Beep with constants + +BEEP 1, 0.0 + diff --git a/tests/functional/opt1_beep_var.asm b/tests/functional/opt1_beep_var.asm new file mode 100644 index 000000000..31fe9622c --- /dev/null +++ b/tests/functional/opt1_beep_var.asm @@ -0,0 +1,104 @@ + org 32768 +__START_PROGRAM: + di + push ix + push iy + exx + push hl + exx + ld hl, 0 + add hl, sp + ld (__CALL_BACK__), hl + ei + ld a, 000h + ld de, 00000h + ld bc, 00000h + push bc + push de + push af + ld a, (_a) + ld de, (_a + 1) + ld bc, (_a + 3) + call BEEP + ld hl, 0 + ld b, h + ld c, l +__END_PROGRAM: + di + ld hl, (__CALL_BACK__) + ld sp, hl + exx + pop hl + exx + pop iy + pop ix + ei + ret +__CALL_BACK__: + DEFW 0 +#line 1 "beep.asm" +#line 1 "stackf.asm" + ; ------------------------------------------------------------- + ; Functions to manage FP-Stack of the ZX Spectrum ROM CALC + ; ------------------------------------------------------------- + __FPSTACK_PUSH EQU 2AB6h ; Stores an FP number into the ROM FP stack (A, ED CB) + __FPSTACK_POP EQU 2BF1h ; Pops an FP number out of the ROM FP stack (A, ED CB) +__FPSTACK_PUSH2: ; Pushes Current A ED CB registers and top of the stack on (SP + 4) + ; Second argument to push into the stack calculator is popped out of the stack + ; Since the caller routine also receives the parameters into the top of the stack + ; four bytes must be removed from SP before pop them out + call __FPSTACK_PUSH ; Pushes A ED CB into the FP-STACK + exx + pop hl ; Caller-Caller return addr + exx + pop hl ; Caller return addr + pop af + pop de + pop bc + push hl ; Caller return addr + exx + push hl ; Caller-Caller return addr + exx + jp __FPSTACK_PUSH +__FPSTACK_I16: ; Pushes 16 bits integer in HL into the FP ROM STACK + ; This format is specified in the ZX 48K Manual + ; You can push a 16 bit signed integer as + ; 0 SS LL HH 0, being SS the sign and LL HH the low + ; and High byte respectively + ld a, h + rla ; sign to Carry + sbc a, a ; 0 if positive, FF if negative + ld e, a + ld d, l + ld c, h + xor a + ld b, a + jp __FPSTACK_PUSH +#line 2 "beep.asm" +BEEP: ; The beep command, as in BASIC + ; Duration in C,ED,LH (float) + ; Pitch in top of the stack + CALL __FPSTACK_PUSH + pop hl ; RET address + pop af + pop de + pop bc ; Recovers PITCH from the stack + push hl ; CALLEE, now ret addr in top of the stack + CALL __FPSTACK_PUSH ; Pitch onto the FP stack + push ix ; BEEP routine modifies IX. We have to preserve it + call 03F8h + pop ix + ret +#line 28 "opt1_beep_var.bas" +ZXBASIC_USER_DATA: +_a: + DEFB 81h + DEFB 00h + DEFB 00h + DEFB 00h + DEFB 00h + ; Defines DATA END --> HEAP size is 0 +ZXBASIC_USER_DATA_END EQU ZXBASIC_MEM_HEAP + ; Defines USER DATA Length in bytes +ZXBASIC_USER_DATA_LEN EQU ZXBASIC_USER_DATA_END - ZXBASIC_USER_DATA + END diff --git a/tests/functional/opt1_beep_var.bas b/tests/functional/opt1_beep_var.bas new file mode 100644 index 000000000..11d706ffb --- /dev/null +++ b/tests/functional/opt1_beep_var.bas @@ -0,0 +1,5 @@ +' Beep with constants + +DIM a as Float = 1 +BEEP a, 0.0 +