diff --git a/arch/__init__.py b/arch/__init__.py index d5585d024..2b4e715b0 100755 --- a/arch/__init__.py +++ b/arch/__init__.py @@ -4,11 +4,10 @@ import importlib -from . import zx48k - __all__ = [ 'zx48k', + 'zxnext' ] AVAILABLE_ARCHITECTURES = __all__ diff --git a/arch/zxnext/__init__.py b/arch/zxnext/__init__.py new file mode 100755 index 000000000..5f5aeb583 --- /dev/null +++ b/arch/zxnext/__init__.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim:ts=4:et:sw=4: + +from . import beep +from .translator import * # noqa + +import api.global_ +from api.constants import TYPE + + +__all__ = [ + 'beep', +] + + +# ----------------------------------------- +# Arch initialization setup +# ----------------------------------------- +api.global_.PARAM_ALIGN = 2 # Z80 param align +api.global_.BOUND_TYPE = TYPE.uinteger +api.global_.SIZE_TYPE = TYPE.ubyte +api.global_.PTR_TYPE = TYPE.uinteger +api.global_.STR_INDEX_TYPE = TYPE.uinteger +api.global_.MIN_STRSLICE_IDX = 0 # Min. string slicing position +api.global_.MAX_STRSLICE_IDX = 65534 # Max. string slicing position diff --git a/arch/zxnext/backend/__16bit.py b/arch/zxnext/backend/__16bit.py new file mode 100644 index 000000000..f13c50daa --- /dev/null +++ b/arch/zxnext/backend/__16bit.py @@ -0,0 +1,1006 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# -------------------------------------------------------------- +# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa +# (a.k.a. Boriel, http://www.boriel.com) +# +# This module contains 8 bit boolean, arithmetic and +# comparation intermediate-code traductions +# -------------------------------------------------------------- + +from .__common import REQUIRES, is_int, log2, is_2n, _int_ops, tmp_label +from .__8bit import _8bit_oper + + +# ----------------------------------------------------- +# 16 bits operands +# ----------------------------------------------------- +def int16(op): + ''' Returns a 16 bit operand converted to 16 bits unsigned int. + Negative numbers are returned in 2 complement. + ''' + return int(op) & 0xFFFF + + +def _16bit_oper(op1, op2=None, reversed=False): + ''' Returns pop sequence for 16 bits operands + 1st operand in HL, 2nd operand in DE + + For subtraction, division, etc. you can swap operators extraction order + by setting reversed to True + ''' + output = [] + + if op1 is not None: + op1 = str(op1) # always to str + + if op2 is not None: + op2 = str(op2) # always to str + + if op2 is not None and reversed: + op1, op2 = op2, op1 + + op = op1 + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + if is_int(op): + op = int(op) + + if indirect: + output.append('ld hl, (%i)' % op) + else: + output.append('ld hl, %i' % int16(op)) + else: + if immediate: + if indirect: + output.append('ld hl, (%s)' % op) + else: + output.append('ld hl, %s' % op) + else: + if op[0] == '_': + output.append('ld hl, (%s)' % op) + else: + output.append('pop hl') + + if indirect: + output.append('ld a, (hl)') + output.append('inc hl') + output.append('ld h, (hl)') + output.append('ld l, a') + + if op2 is None: + return output + + if not reversed: + tmp = output + output = [] + + op = op2 + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + if is_int(op): + op = int(op) + + if indirect: + output.append('ld de, (%i)' % op) + else: + output.append('ld de, %i' % int16(op)) + else: + if immediate: + output.append('ld de, %s' % op) + else: + if op[0] == '_': + output.append('ld de, (%s)' % op) + else: + output.append('pop de') + + if indirect: + output.append('call __LOAD_DE_DE') # DE = (DE) + REQUIRES.add('lddede.asm') + + if not reversed: + output.extend(tmp) + + return output + + +# ----------------------------------------------------- +# Arithmetic operations +# ----------------------------------------------------- + +def _add16(ins): + ''' Pops last 2 bytes from the stack and adds them. + Then push the result onto the stack. + + + Optimizations: + * If any of the operands is ZERO, + then do NOTHING: A + 0 = 0 + A = A + + * If any of the operands is < 4, then + INC is used + + * If any of the operands is > (65531) (-4), then + DEC is used + ''' + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + op2 = int16(op2) + output = _16bit_oper(op1) + + if op2 == 0: + output.append('push hl') + return output # ADD HL, 0 => NOTHING + + if op2 < 4: + output.extend(['inc hl'] * op2) # ADD HL, 2 ==> inc hl; inc hl + output.append('push hl') + return output + + if op2 > 65531: # (between -4 and 0) + output.extend(['dec hl'] * (0x10000 - op2)) + output.append('push hl') + return output + + output.append('ld de, %i' % op2) + output.append('add hl, de') + output.append('push hl') + return output + + if op2[0] == '_': # stack optimization + op1, op2 = op2, op1 + + output = _16bit_oper(op1, op2) + output.append('add hl, de') + output.append('push hl') + return output + + +def _sub16(ins): + ''' Pops last 2 words from the stack and subtract them. + Then push the result onto the stack. Top of the stack is + subtracted Top -1 + + Optimizations: + * If 2nd op is ZERO, + then do NOTHING: A - 0 = A + + * If any of the operands is < 4, then + DEC is used + + * If any of the operands is > 65531 (-4..-1), then + INC is used + ''' + op1, op2 = tuple(ins.quad[2:4]) + + if is_int(op2): + op = int16(op2) + output = _16bit_oper(op1) + + if op == 0: + output.append('push hl') + return output + + if op < 4: + output.extend(['dec hl'] * op) + output.append('push hl') + return output + + if op > 65531: + output.extend(['inc hl'] * (0x10000 - op)) + output.append('push hl') + return output + + output.append('ld de, -%i' % op) + output.append('add hl, de') + output.append('push hl') + return output + + if op2[0] == '_': # Optimization when 2nd operand is an id + rev = True + op1, op2 = op2, op1 + else: + rev = False + + output = _16bit_oper(op1, op2, rev) + output.append('or a') + output.append('sbc hl, de') + output.append('push hl') + return output + + +def _mul16(ins): + ''' Multiplies tow last 16bit values on top of the stack and + and returns the value on top of the stack + + Optimizations: + * If any of the ops is ZERO, + then do A = 0 ==> XOR A, cause A * 0 = 0 * A = 0 + + * If any ot the ops is ONE, do NOTHING + A * 1 = 1 * A = A + + * If B is 2^n and B < 16 => Shift Right n + ''' + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: # If any of the operands is constant + op1, op2 = _int_ops(op1, op2) # put the constant one the 2nd + output = _16bit_oper(op1) + + if op2 == 0: # A * 0 = 0 * A = 0 + if op1[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + output.append('ld hl, 0') + output.append('push hl') + return output + + if op2 == 1: # A * 1 = 1 * A == A => Do nothing + output.append('push hl') + return output + + if op2 == 0xFFFF: # This is the same as (-1) + output.append('call __NEGHL') + output.append('push hl') + REQUIRES.add('neg16.asm') + return output + + if is_2n(op2) and log2(op2) < 4: + output.extend(['add hl, hl'] * int(log2(op2))) + output.append('push hl') + return output + + output.append('ld de, %i' % op2) + else: + if op2[0] == '_': # stack optimization + op1, op2 = op2, op1 + + output = _16bit_oper(op1, op2) + + output.append('call __MUL16_FAST') # Inmmediate + output.append('push hl') + REQUIRES.add('mul16.asm') + return output + + +def _divu16(ins): + ''' Divides 2 16bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 1 then + do nothing + + * If 2nd op is 2 then + Shift Right Logical + ''' + op1, op2 = tuple(ins.quad[2:]) + if is_int(op1) and int(op1) == 0: # 0 / A = 0 + if op2[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + else: + output = _16bit_oper(op2) # Normalize stack + + output.append('ld hl, 0') + output.append('push hl') + return output + + if is_int(op2): + op = int16(op2) + output = _16bit_oper(op1) + + if op2 == 0: # A * 0 = 0 * A = 0 + if op1[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + output.append('ld hl, 0') + output.append('push hl') + return output + + if op == 1: + output.append('push hl') + return output + + if op == 2: + output.append('srl h') + output.append('rr l') + output.append('push hl') + return output + + output.append('ld de, %i' % op) + else: + if op2[0] == '_': # Optimization when 2nd operand is an id + rev = True + op1, op2 = op2, op1 + else: + rev = False + output = _16bit_oper(op1, op2, rev) + + output.append('call __DIVU16') + output.append('push hl') + REQUIRES.add('div16.asm') + return output + + +def _divi16(ins): + ''' Divides 2 16bit signed integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 1 then + do nothing + + * If 2nd op is -1 then + do NEG + + * If 2nd op is 2 then + Shift Right Arithmetic + ''' + op1, op2 = tuple(ins.quad[2:]) + if is_int(op1) and int(op1) == 0: # 0 / A = 0 + if op2[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + else: + output = _16bit_oper(op2) # Normalize stack + + output.append('ld hl, 0') + output.append('push hl') + return output + + if is_int(op2): + op = int16(op2) + output = _16bit_oper(op1) + + if op == 1: + output.append('push hl') + return output + + if op == -1: + output.append('call __NEGHL') + output.append('push hl') + REQUIRES.add('neg16.asm') + return output + + if op == 2: + output.append('sra h') + output.append('rr l') + output.append('push hl') + return output + + output.append('ld de, %i' % op) + else: + if op2[0] == '_': # Optimization when 2nd operand is an id + rev = True + op1, op2 = op2, op1 + else: + rev = False + output = _16bit_oper(op1, op2, rev) + + output.append('call __DIVI16') + output.append('push hl') + REQUIRES.add('div16.asm') + return output + + +def _modu16(ins): + ''' Reminder of div. 2 16bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd operand is 1 => Return 0 + * If 2nd operand = 2^n => do AND (2^n - 1) + ''' + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + op2 = int16(op2) + output = _16bit_oper(op1) + + if op2 == 1: + if op2[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + + output.append('ld hl, 0') + output.append('push hl') + return output + + if is_2n(op2): + k = op2 - 1 + if op2 > 255: # only affects H + output.append('ld a, h') + output.append('and %i' % (k >> 8)) + output.append('ld h, a') + else: + output.append('ld h, 0') # High part goes 0 + output.append('ld a, l') + output.append('and %i' % (k % 0xFF)) + output.append('ld l, a') + + output.append('push hl') + return output + + output.append('ld de, %i' % op2) + else: + output = _16bit_oper(op1, op2) + + output.append('call __MODU16') + output.append('push hl') + REQUIRES.add('div16.asm') + return output + + +def _modi16(ins): + ''' Reminder of div 2 16bit signed integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd operand is 1 => Return 0 + * If 2nd operand = 2^n => do AND (2^n - 1) + ''' + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + op2 = int16(op2) + output = _16bit_oper(op1) + + if op2 == 1: + if op2[0] in ('_', '$'): + output = [] # Optimization: Discard previous op if not from the stack + + output.append('ld hl, 0') + output.append('push hl') + return output + + if is_2n(op2): + k = op2 - 1 + if op2 > 255: # only affects H + output.append('ld a, h') + output.append('and %i' % (k >> 8)) + output.append('ld h, a') + else: + output.append('ld h, 0') # High part goes 0 + output.append('ld a, l') + output.append('and %i' % (k % 0xFF)) + output.append('ld l, a') + + output.append('push hl') + return output + + output.append('ld de, %i' % op2) + else: + output = _16bit_oper(op1, op2) + + output.append('call __MODI16') + output.append('push hl') + REQUIRES.add('div16.asm') + return output + + +def _ltu16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit unsigned version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('or a') + output.append('sbc hl, de') + output.append('sbc a, a') + output.append('push af') + return output + + +def _lti16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit signed version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('call __LTI16') + output.append('push af') + REQUIRES.add('lti16.asm') + return output + + +def _gtu16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit unsigned version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3], reversed=True) + output.append('or a') + output.append('sbc hl, de') + output.append('sbc a, a') + output.append('push af') + return output + + +def _gti16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit signed version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3], reversed=True) + output.append('call __LTI16') + output.append('push af') + REQUIRES.add('lti16.asm') + return output + + +def _leu16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit unsigned version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3], reversed=True) + output.append('or a') + output.append('sbc hl, de') # Carry if A > B + output.append('ccf') # Negates the result => Carry if A <= B + output.append('sbc a, a') + output.append('push af') + return output + + +def _lei16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit signed version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('call __LEI16') + output.append('push af') + REQUIRES.add('lei16.asm') + return output + + +def _geu16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit unsigned version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('or a') + output.append('sbc hl, de') + output.append('ccf') + output.append('sbc a, a') + output.append('push af') + return output + + +def _gei16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit signed version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3], reversed=True) + output.append('call __LEI16') + output.append('push af') + REQUIRES.add('lei16.asm') + return output + + +def _eq16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand == 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit un/signed version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('call __EQ16') + output.append('push af') + REQUIRES.add('eq16.asm') + + return output + + +def _ne16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand != 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 16 bit un/signed version + ''' + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('or a') # Resets carry flag + output.append('sbc hl, de') + output.append('ld a, h') + output.append('or l') + output.append('push af') + + return output + + +def _or16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand OR (logical) 2nd operand (top of the stack), + pushes 0 if False, 1 if True. + + 16 bit un/signed version + + Optimizations: + + If any of the operators are constants: Returns either 0 or + the other operand + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + if op2 == 0: + output = _16bit_oper(op1) + output.append('ld a, h') + output.append('or l') # Convert x to Boolean + output.append('push af') + return output # X or False = X + + output = _16bit_oper(op1) + output.append('ld a, 0FFh') # X or True = True + output.append('push af') + return output + + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('ld a, h') + output.append('or l') + output.append('or d') + output.append('or e') + output.append('push af') + return output + + +def _bor16(ins): + ''' Pops top 2 operands out of the stack, and performs + 1st operand OR (bitwise) 2nd operand (top of the stack), + pushes result (16 bit in HL). + + 16 bit un/signed version + + Optimizations: + + If any of the operators are constants: Returns either 0 or + the other operand + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _16bit_oper(op1) + if op2 == 0: # X | 0 = X + output.append('push hl') + return output + + if op2 == 0xFFFF: # X & 0xFFFF = 0xFFFF + output.append('ld hl, 0FFFFh') + output.append('push hl') + return output + + output = _16bit_oper(op1, op2) + output.append('call __BOR16') + output.append('push hl') + REQUIRES.add('bor16.asm') + return output + + +def _xor16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand XOR (logical) 2nd operand (top of the stack), + pushes 0 if False, 1 if True. + + 16 bit un/signed version + + Optimizations: + + If any of the operators are constants: Returns either 0 or + the other operand + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + output = _16bit_oper(op1) + + if op2 == 0: # X xor False = X + output.append('ld a, h') + output.append('or l') + output.append('push af') + return output + + # X xor True = NOT X + output.append('ld a, h') + output.append('or l') + output.append('sub 1') + output.append('sbc a, a') + output.append('push af') + return output + + output = _16bit_oper(ins.quad[2], ins.quad[3]) + output.append('call __XOR16') + output.append('push af') + REQUIRES.add('xor16.asm') + return output + + +def _bxor16(ins): + ''' Pops top 2 operands out of the stack, and performs + 1st operand XOR (bitwise) 2nd operand (top of the stack), + pushes result (16 bit in HL). + + 16 bit un/signed version + + Optimizations: + + If any of the operators are constants: Returns either 0 or + the other operand + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _16bit_oper(op1) + if op2 == 0: # X ^ 0 = X + output.append('push hl') + return output + + if op2 == 0xFFFF: # X ^ 0xFFFF = bNOT X + output.append('call __NEGHL') + output.append('push hl') + REQUIRES.add('neg16.asm') + return output + + output = _16bit_oper(op1, op2) + output.append('call __BXOR16') + output.append('push hl') + REQUIRES.add('bxor16.asm') + return output + + +def _and16(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand AND (logical) 2nd operand (top of the stack), + pushes 0 if False, 1 if True. + + 16 bit un/signed version + + Optimizations: + + If any of the operators are constants: Returns either 0 or + the other operand + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _16bit_oper(op1) + if op2 != 0: + output.append('ld a, h') + output.append('or l') + output.append('push af') + return output # X and True = X + + output = _16bit_oper(op1) + output.append('xor a') # X and False = False + output.append('push af') + return output + + output = _16bit_oper(op1, op2) + output.append('call __AND16') + output.append('push af') + REQUIRES.add('and16.asm') + return output + + +def _band16(ins): + ''' Pops top 2 operands out of the stack, and performs + 1st operand AND (bitwise) 2nd operand (top of the stack), + pushes result (16 bit in HL). + + 16 bit un/signed version + + Optimizations: + + If any of the operators are constants: Returns either 0 or + the other operand + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + if op2 == 0xFFFF: # X & 0xFFFF = X + return [] + + if op2 == 0: # X & 0 = 0 + output = _16bit_oper(op1) + output.append('ld hl, 0') + output.append('push hl') + return output + + output = _16bit_oper(op1, op2) + output.append('call __BAND16') + output.append('push hl') + REQUIRES.add('band16.asm') + return output + + +def _not16(ins): + ''' Negates top (Logical NOT) of the stack (16 bits in HL) + ''' + output = _16bit_oper(ins.quad[2]) + output.append('ld a, h') + output.append('or l') + output.append('sub 1') + output.append('sbc a, a') + output.append('push af') + return output + + +def _bnot16(ins): + ''' Negates top (Bitwise NOT) of the stack (16 bits in HL) + ''' + output = _16bit_oper(ins.quad[2]) + output.append('call __BNOT16') + output.append('push hl') + REQUIRES.add('bnot16.asm') + return output + + +def _neg16(ins): + ''' Negates top of the stack (16 bits in HL) + ''' + output = _16bit_oper(ins.quad[2]) + output.append('call __NEGHL') + output.append('push hl') + REQUIRES.add('neg16.asm') + return output + + +def _abs16(ins): + ''' Absolute value of top of the stack (16 bits in HL) + ''' + output = _16bit_oper(ins.quad[2]) + output.append('call __ABS16') + output.append('push hl') + REQUIRES.add('abs16.asm') + return output + + +def _shru16(ins): + ''' Logical right shift 16bit unsigned integer. + The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 0 then + do nothing + + * If 2nd op is 1 + Shift Right Arithmetic + ''' + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op = int16(op2) + if op == 0: + return [] + + output = _16bit_oper(op1) + if op == 1: + output.append('srl h') + output.append('rr l') + output.append('push hl') + return output + + output.append('ld b, %i' % op) + else: + output = _8bit_oper(op2) + output.append('ld b, a') + output.extend(_16bit_oper(op1)) + + label = tmp_label() + output.append('%s:' % label) + output.append('srl h') + output.append('rr l') + output.append('djnz %s' % label) + output.append('push hl') + return output + + +def _shri16(ins): + ''' Arithmetical right shift 16bit signed integer. + The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 0 then + do nothing + + * If 2nd op is 1 + Shift Right Arithmetic + ''' + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op = int16(op2) + if op == 0: + return [] + + output = _16bit_oper(op1) + if op == 1: + output.append('srl h') + output.append('rr l') + output.append('push hl') + return output + + output.append('ld b, %i' % op) + else: + output = _8bit_oper(op2) + output.append('ld b, a') + output.extend(_16bit_oper(op1)) + + label = tmp_label() + output.append('%s:' % label) + output.append('sra h') + output.append('rr l') + output.append('djnz %s' % label) + output.append('push hl') + return output + + +def _shl16(ins): + ''' Logical/aritmetical left shift 16bit (un)signed integer. + The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 0 then + do nothing + + * If 2nd op is lower than 6 + unroll lop + ''' + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op = int16(op2) + if op == 0: + return [] + + output = _16bit_oper(op1) + if op < 6: + output.extend(['add hl, hl'] * op) + output.append('push hl') + return output + + output.append('ld b, %i' % op) + else: + output = _8bit_oper(op2) + output.append('ld b, a') + output.extend(_16bit_oper(op1)) + + label = tmp_label() + output.append('%s:' % label) + output.append('add hl, hl') + output.append('djnz %s' % label) + output.append('push hl') + return output diff --git a/arch/zxnext/backend/__32bit.py b/arch/zxnext/backend/__32bit.py new file mode 100644 index 000000000..1a81f803e --- /dev/null +++ b/arch/zxnext/backend/__32bit.py @@ -0,0 +1,868 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim:ts=4:et:sw=4: + +# -------------------------------------------------------------- +# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa +# (a.k.a. Boriel, http://www.boriel.com) +# +# This module contains 8 bit boolean, arithmetic and +# comparation intermediate-code traductions +# -------------------------------------------------------------- + +from .__common import REQUIRES, is_int, _int_ops, tmp_label +from .__8bit import _8bit_oper + + +# ----------------------------------------------------- +# 32 bits operands +# ----------------------------------------------------- + + +def int32(op): + """ Returns a 32 bit operand converted to 32 bits unsigned int. + Negative numbers are returned in 2 complement. + + The result is returned in a tuple (DE, HL) => High16, Low16 + """ + result = int(op) & 0xFFFFFFFF + return (result >> 16, result & 0xFFFF) + + +def _32bit_oper(op1, op2=None, reversed=False, preserveHL=False): + """ Returns pop sequence for 32 bits operands + 1st operand in HLDE, 2nd operand remains in the stack + + Now it does support operands inversion calling __SWAP32. + + However, if 1st operand is integer (immediate) or indirect, the stack + will be rearranged, so it contains a 32 bit pushed parameter value for the + subroutine to be called. + + If preserveHL is True, then BC will be used instead of HL for lower part + for the 1st operand. + """ + output = [] + + if op1 is not None: + op1 = str(op1) + + if op2 is not None: + op2 = str(op2) + + op = op2 if op2 is not None else op1 + + int1 = False # whether op1 (2nd operand) is integer + + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + hl = 'hl' if not preserveHL and not indirect else 'bc' + + if is_int(op): + int1 = True + op = int(op) + + if indirect: + if immediate: + output.append('ld hl, %i' % op) + else: + output.append('ld hl, (%i)' % op) + + output.append('call __ILOAD32') + REQUIRES.add('iload32.asm') + + if preserveHL: + output.append('ld b, h') + output.append('ld c, l') + else: + DE, HL = int32(op) + output.append('ld de, %i' % DE) + output.append('ld %s, %i' % (hl, HL)) + else: + if op[0] == '_': + if immediate: + output.append('ld %s, %s' % (hl, op)) + else: + output.append('ld %s, (%s)' % (hl, op)) + else: + if immediate: + output.append('ld %s, (%s) & 0xFFFF' % (hl, op)) + else: + output.append('pop %s' % hl) + + if indirect: + output.append('call __ILOAD32') + REQUIRES.add('iload32.asm') + + if preserveHL: + output.append('ld b, h') + output.append('ld c, l') + else: + if op[0] == '_': + output.append('ld de, (%s + 2)' % op) + else: + if immediate: + output.append('ld de, (%s) >> 16' % op) + else: + output.append('pop de') + + if op2 is not None: + op = op1 + + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + if is_int(op): + op = int(op) + + if indirect: + output.append('exx') + if immediate: + output.append('ld hl, %i' % (op & 0xFFFF)) + else: + output.append('ld hl, (%i)' % (op & 0xFFFF)) + + output.append('call __ILOAD32') + output.append('push de') + output.append('push hl') + output.append('exx') + REQUIRES.add('iload32.asm') + else: + DE, HL = int32(op) + output.append('ld bc, %i' % DE) + output.append('push bc') + output.append('ld bc, %i' % HL) + output.append('push bc') + else: + if indirect: + output.append('exx') # uses alternate set to put it on the stack + if op[0] == '_': + if immediate: + output.append('ld hl, %s' % op) + else: + output.append('ld hl, (%s)' % op) + else: + output.append('pop hl') # Pointers are only 16 bits *** + + output.append('call __ILOAD32') + output.append('push de') + output.append('push hl') + output.append('exx') + REQUIRES.add('iload32.asm') + elif immediate: + output.append('ld bc, (%s) >> 16' % op) + output.append('push bc') + output.append('ld bc, (%s) & 0xFFFF' % op) + output.append('push bc') + elif op[0] == '_': # an address + if int1 or op1[0] == '_': # If previous op was integer, we can use hl in advance + tmp = output + output = [] + output.append('ld hl, (%s + 2)' % op) + output.append('push hl') + output.append('ld hl, (%s)' % op) + output.append('push hl') + output.extend(tmp) + else: + output.append('ld bc, (%s + 2)' % op) + output.append('push bc') + output.append('ld bc, (%s)' % op) + output.append('push bc') + else: + pass # 2nd operand remains in the stack + + if op2 is not None and reversed: + output.append('call __SWAP32') + REQUIRES.add('swap32.asm') + + return output + + +# ----------------------------------------------------- +# Arithmetic operations +# ----------------------------------------------------- + +def _add32(ins): + """ Pops last 2 bytes from the stack and adds them. + Then push the result onto the stack. + + Optimizations: + * If any of the operands is ZERO, + then do NOTHING: A + 0 = 0 + A = A + """ + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2) is not None: + o1, o2 = _int_ops(op1, op2) + + if int(o2) == 0: # A + 0 = 0 + A = A => Do Nothing + output = _32bit_oper(o1) + output.append('push de') + output.append('push hl') + return output + + if op1[0] == '_' and op2[0] != '_': + op1, op2 = op2, op1 # swap them + + if op2[0] == '_': + output = _32bit_oper(op1) + output.append('ld bc, (%s)' % op2) + output.append('add hl, bc') + output.append('ex de, hl') + output.append('ld bc, (%s + 2)' % op2) + output.append('adc hl, bc') + output.append('push hl') + output.append('push de') + return output + + output = _32bit_oper(op1, op2) + output.append('pop bc') + output.append('add hl, bc') + output.append('ex de, hl') + output.append('pop bc') + output.append('adc hl, bc') + output.append('push hl') # High and low parts are reversed + output.append('push de') + + return output + + +def _sub32(ins): + """ Pops last 2 dwords from the stack and subtract them. + Then push the result onto the stack. + NOTE: The operation is TOP[0] = TOP[-1] - TOP[0] + + If TOP[0] is 0, nothing is done + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + if int(op2) == 0: # A - 0 = A => Do Nothing + output = _32bit_oper(op1) + output.append('push de') + output.append('push hl') + return output + + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + + output = _32bit_oper(op1, op2, rev) + output.append('call __SUB32') + output.append('push de') + output.append('push hl') + REQUIRES.add('sub32.asm') + return output + + +def _mul32(ins): + """ Multiplies two last 32bit values on top of the stack and + and returns the value on top of the stack + + Optimizations done: + + * If any operand is 1, do nothing + * If any operand is 0, push 0 + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2): + op1, op2 = _int_ops(op1, op2) + output = _32bit_oper(op1) + + if op2 == 1: + output.append('push de') + output.append('push hl') + return output # A * 1 = Nothing + + if op2 == 0: + output.append('ld hl, 0') + output.append('push hl') + output.append('push hl') + return output + + output = _32bit_oper(op1, op2) + output.append('call __MUL32') # Inmmediate + output.append('push de') + output.append('push hl') + REQUIRES.add('mul32.asm') + return output + + +def _divu32(ins): + """ Divides 2 32bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + + * If 2nd operand is 1, do nothing + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + if int(op2) == 1: + output = _32bit_oper(op1) + output.append('push de') + output.append('push hl') + return output + + rev = is_int(op1) or op1[0] == 't' or op2[0] != 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __DIVU32') + output.append('push de') + output.append('push hl') + REQUIRES.add('div32.asm') + return output + + +def _divi32(ins): + """ Divides 2 32bit signed integers. The result is pushed onto the stack. + + Optimizations: + + * If 2nd operand is 1, do nothing + * If 2nd operand is -1, do NEG32 + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + if int(op2) == 1: + output = _32bit_oper(op1) + output.append('push de') + output.append('push hl') + return output + + if int(op2) == -1: + return _neg32(ins) + + rev = is_int(op1) or op1[0] == 't' or op2[0] != 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __DIVI32') + output.append('push de') + output.append('push hl') + REQUIRES.add('div32.asm') + return output + + +def _modu32(ins): + """ Reminder of div. 2 32bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + + * If 2nd op is 1. Returns 0 + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + if int(op2) == 1: + output = _32bit_oper(op1) + output.append('ld hl, 0') + output.append('push hl') + output.append('push hl') + return output + + rev = is_int(op1) or op1[0] == 't' or op2[0] != 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __MODU32') + output.append('push de') + output.append('push hl') + REQUIRES.add('div32.asm') + return output + + +def _modi32(ins): + """ Reminder of div. 2 32bit signed integers. The result is pushed onto the stack. + + Optimizations: + + * If 2nd op is 1. Returns 0 + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + if int(op2) == 1: + output = _32bit_oper(op1) + output.append('ld hl, 0') + output.append('push hl') + output.append('push hl') + return output + + rev = is_int(op1) or op1[0] == 't' or op2[0] != 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __MODI32') + output.append('push de') + output.append('push hl') + REQUIRES.add('div32.asm') + return output + + +def _ltu32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit unsigned version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __SUB32') + output.append('sbc a, a') + output.append('push af') + REQUIRES.add('sub32.asm') + return output + + +def _lti32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit signed version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __LTI32') + output.append('push af') + REQUIRES.add('lti32.asm') + return output + + +def _gtu32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit unsigned version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('pop bc') + output.append('or a') + output.append('sbc hl, bc') + output.append('ex de, hl') + output.append('pop de') + output.append('sbc hl, de') + output.append('sbc a, a') + output.append('push af') + return output + + +def _gti32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit signed version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __LEI32') # Checks A <= B ? + output.append('sub 1') # Carry if A = 0 (False) + output.append('sbc a, a') # Negates => A > B ? + output.append('push af') + REQUIRES.add('lei32.asm') + return output + + +def _leu32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit unsigned version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('pop bc') + output.append('or a') + output.append('sbc hl, bc') + output.append('ex de, hl') + output.append('pop de') + output.append('sbc hl, de') # Carry if A > B + output.append('ccf') # Negates result => Carry if A <= B + output.append('sbc a, a') + output.append('push af') + return output + + +def _lei32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit signed version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __LEI32') + output.append('push af') + REQUIRES.add('lei32.asm') + return output + + +def _geu32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit unsigned version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __SUB32') # Carry if A < B + output.append('ccf') # Negates result => Carry if A >= B + output.append('sbc a, a') + output.append('push af') + REQUIRES.add('sub32.asm') + return output + + +def _gei32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit signed version + """ + op1, op2 = tuple(ins.quad[2:]) + rev = op1[0] != 't' and not is_int(op1) and op2[0] == 't' + output = _32bit_oper(op1, op2, rev) + output.append('call __LTI32') # A = (a < b) + output.append('sub 1') # Carry if !(a < b) + output.append('sbc a, a') # A = !(a < b) = (a >= b) + output.append('push af') + REQUIRES.add('lti32.asm') + return output + + +def _eq32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand == 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __EQ32') + output.append('push af') + REQUIRES.add('eq32.asm') + return output + + +def _ne32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand != 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __EQ32') + output.append('sub 1') # Carry if A = 0 (False) + output.append('sbc a, a') # Negates => A > B ? + output.append('push af') + REQUIRES.add('eq32.asm') + return output + + +def _or32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand OR (Logical) 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __OR32') + output.append('push af') + REQUIRES.add('or32.asm') + return output + + +def _bor32(ins): + """ Pops top 2 operands out of the stack, and checks + if the 1st operand OR (Bitwise) 2nd operand (top of the stack). + Pushes result DE (high) HL (low) + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __BOR32') + output.append('push de') + output.append('push hl') + REQUIRES.add('bor32.asm') + return output + + +def _xor32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand XOR (Logical) 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __XOR32') + output.append('push af') + REQUIRES.add('xor32.asm') + return output + + +def _bxor32(ins): + """ Pops top 2 operands out of the stack, and checks + if the 1st operand XOR (Bitwise) 2nd operand (top of the stack). + Pushes result DE (high) HL (low) + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __BXOR32') + output.append('push de') + output.append('push hl') + REQUIRES.add('bxor32.asm') + return output + + +def _and32(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand AND (Logical) 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + + if _int_ops(op1, op2): + op1, op2 = _int_ops(op1, op2) + + if op2 == 0: # X and False = False + if str(op1)[0] == 't': # a temporary term (stack) + output = _32bit_oper(op1) # Remove op1 from the stack + else: + output = [] + output.append('xor a') + output.append('push af') + return output + + # For X and TRUE = X we do nothing as we have to convert it to boolean + # which is a rather expensive instruction + + output = _32bit_oper(op1, op2) + output.append('call __AND32') + output.append('push af') + REQUIRES.add('and32.asm') + return output + + +def _band32(ins): + """ Pops top 2 operands out of the stack, and checks + if the 1st operand AND (Bitwise) 2nd operand (top of the stack). + Pushes result DE (high) HL (low) + + 32 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + output = _32bit_oper(op1, op2) + output.append('call __BAND32') + output.append('push de') + output.append('push hl') + REQUIRES.add('band32.asm') + return output + + +def _not32(ins): + """ Negates top (Logical NOT) of the stack (32 bits in DEHL) + """ + output = _32bit_oper(ins.quad[2]) + output.append('call __NOT32') + output.append('push af') + REQUIRES.add('not32.asm') + return output + + +def _bnot32(ins): + """ Negates top (Bitwise NOT) of the stack (32 bits in DEHL) + """ + output = _32bit_oper(ins.quad[2]) + output.append('call __BNOT32') + output.append('push de') + output.append('push hl') + REQUIRES.add('bnot32.asm') + return output + + +def _neg32(ins): + """ Negates top of the stack (32 bits in DEHL) + """ + output = _32bit_oper(ins.quad[2]) + output.append('call __NEG32') + output.append('push de') + output.append('push hl') + REQUIRES.add('neg32.asm') + return output + + +def _abs32(ins): + """ Absolute value of top of the stack (32 bits in DEHL) + """ + output = _32bit_oper(ins.quad[2]) + output.append('call __ABS32') + output.append('push de') + output.append('push hl') + REQUIRES.add('abs32.asm') + return output + + +def _shru32(ins): + """ Logical Right shift 32bit unsigned integers. + The result is pushed onto the stack. + + Optimizations: + + * If 2nd operand is 0, do nothing + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + output = _32bit_oper(op1) + + if int(op2) == 0: + output.append('push de') + output.append('push hl') + return output + + if int(op2) > 1: + label = tmp_label() + output.append('ld b, %s' % op2) + output.append('%s:' % label) + output.append('call __SHRL32') + output.append('djnz %s' % label) + else: + output.append('call __SHRL32') + + output.append('push de') + output.append('push hl') + REQUIRES.add('shrl32.asm') + return output + + output = _8bit_oper(op2) + output.append('ld b, a') + output.extend(_32bit_oper(op1)) + label = tmp_label() + output.append('%s:' % label) + output.append('call __SHRL32') + output.append('djnz %s' % label) + output.append('push de') + output.append('push hl') + REQUIRES.add('shrl32.asm') + return output + + +def _shri32(ins): + """ Logical Right shift 32bit unsigned integers. + The result is pushed onto the stack. + + Optimizations: + + * If 2nd operand is 0, do nothing + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + output = _32bit_oper(op1) + + if int(op2) == 0: + output.append('push de') + output.append('push hl') + return output + + if int(op2) > 1: + label = tmp_label() + output.append('ld b, %s' % op2) + output.append('%s:' % label) + output.append('call __SHRA32') + output.append('djnz %s' % label) + else: + output.append('call __SHRA32') + + output.append('push de') + output.append('push hl') + REQUIRES.add('shra32.asm') + return output + + output = _8bit_oper(op2) + output.append('ld b, a') + output.extend(_32bit_oper(op1)) + label = tmp_label() + output.append('%s:' % label) + output.append('call __SHRA32') + output.append('djnz %s' % label) + output.append('push de') + output.append('push hl') + REQUIRES.add('shra32.asm') + return output + + +def _shl32(ins): + """ Logical Left shift 32bit unsigned integers. + The result is pushed onto the stack. + + Optimizations: + + * If 2nd operand is 0, do nothing + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + output = _32bit_oper(op1) + + if int(op2) == 0: + output.append('push de') + output.append('push hl') + return output + + if int(op2) > 1: + label = tmp_label() + output.append('ld b, %s' % op2) + output.append('%s:' % label) + output.append('call __SHL32') + output.append('djnz %s' % label) + else: + output.append('call __SHL32') + + output.append('push de') + output.append('push hl') + REQUIRES.add('shl32.asm') + return output + + output = _8bit_oper(op2) + output.append('ld b, a') + output.extend(_32bit_oper(op1)) + label = tmp_label() + output.append('%s:' % label) + output.append('call __SHL32') + output.append('djnz %s' % label) + output.append('push de') + output.append('push hl') + REQUIRES.add('shl32.asm') + return output diff --git a/arch/zxnext/backend/__8bit.py b/arch/zxnext/backend/__8bit.py new file mode 100644 index 000000000..316bcbe46 --- /dev/null +++ b/arch/zxnext/backend/__8bit.py @@ -0,0 +1,1052 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim: ts=4:sw=4:et + +# -------------------------------------------------------------- +# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa +# (a.k.a. Boriel, http://www.boriel.com) +# +# This module contains 8 bit boolean, arithmetic and +# comparation intermediate-code traductions +# -------------------------------------------------------------- + +from .__common import REQUIRES, is_int, is_2n, _int_ops, tmp_label + + +def int8(op): + """ Returns the operator converted to 8 bit unsigned integer. + For signed ones, it returns the 8bit C2 (Two Complement) + """ + return int(op) & 0xFF + + +def _8bit_oper(op1, op2=None, reversed_=False): + """ Returns pop sequence for 8 bits operands + 1st operand in H, 2nd operand in A (accumulator) + + For some operations (like comparisons), you can swap + operands extraction by setting reversed = True + """ + output = [] + + if op2 is not None and reversed_: + tmp = op1 + op1 = op2 + op2 = tmp + + op = op1 + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + if is_int(op): + op = int(op) + + if indirect: + output.append('ld a, (%i)' % op) + else: + if op == 0: + output.append('xor a') + else: + output.append('ld a, %i' % int8(op)) + else: + if immediate: + if indirect: + output.append('ld a, (%s)' % op) + else: + output.append('ld a, %s' % op) + elif op[0] == '_': + if indirect: + idx = 'bc' if reversed_ else 'hl' + output.append('ld %s, (%s)' % (idx, op)) # can't use HL + output.append('ld a, (%s)' % idx) + else: + output.append('ld a, (%s)' % op) + else: + if immediate: + output.append('ld a, %s' % op) + elif indirect: + idx = 'bc' if reversed_ else 'hl' + output.append('pop %s' % idx) + output.append('ld a, (%s)' % idx) + else: + output.append('pop af') + + if op2 is None: + return output + + if not reversed_: + tmp = output + output = [] + + op = op2 + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + if is_int(op): + op = int(op) + + if indirect: + output.append('ld hl, (%i - 1)' % op) + else: + output.append('ld h, %i' % int8(op)) + else: + if immediate: + if indirect: + output.append('ld hl, %s' % op) + output.append('ld h, (hl)') + else: + output.append('ld h, %s' % op) + elif op[0] == '_': + if indirect: + output.append('ld hl, (%s)' % op) + output.append('ld h, (hl)' % op) + else: + output.append('ld hl, (%s - 1)' % op) + else: + output.append('pop hl') + + if indirect: + output.append('ld h, (hl)') + + if not reversed_: + output.extend(tmp) + + return output + + +def _add8(ins): + """ Pops last 2 bytes from the stack and adds them. + Then push the result onto the stack. + + Optimizations: + * If any of the operands is ZERO, + then do NOTHING: A + 0 = 0 + A = A + + * If any of the operands is 1, then + INC is used + + * If any of the operands is -1 (255), then + DEC is used + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) + if op2 == 0: # Nothing to add: A + 0 = A + output.append('push af') + return output + + op2 = int8(op2) + + if op2 == 1: # Adding 1 is just an inc + output.append('inc a') + output.append('push af') + return output + + if op2 == 0xFF: # Adding 255 is just a dec + output.append('dec a') + output.append('push af') + return output + + output.append('add a, %i' % int8(op2)) + output.append('push af') + return output + + if op2[0] == '_': # stack optimization + op1, op2 = op2, op1 + + output = _8bit_oper(op1, op2) + output.append('add a, h') + output.append('push af') + + return output + + +def _sub8(ins): + """ Pops last 2 bytes from the stack and subtract them. + Then push the result onto the stack. Top-1 of the stack is + subtracted Top + _sub8 t1, a, b === t1 <-- a - b + + Optimizations: + * If 2nd op is ZERO, + then do NOTHING: A - 0 = A + + * If 1st operand is 0, then + just do a NEG + + * If any of the operands is 1, then + DEC is used + + * If any of the operands is -1 (255), then + INC is used + """ + + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): # 2nd operand + op2 = int8(op2) + output = _8bit_oper(op1) + + if op2 == 0: + output.append('push af') + return output # A - 0 = A + + op2 = int8(op2) + + if op2 == 1: # A - 1 == DEC A + output.append('dec a') + output.append('push af') + return output + + if op2 == 0xFF: # A - (-1) == INC A + output.append('inc a') + output.append('push af') + return output + + output.append('sub %i' % op2) + output.append('push af') + return output + + if is_int(op1): # 1st operand is numeric? + if int8(op1) == 0: # 0 - A = -A ==> NEG A + output = _8bit_oper(op2) + output.append('neg') + output.append('push af') + return output + + # At this point, even if 1st operand is numeric, proceed + # normally + + if op2[0] == '_': # Optimization when 2nd operand is an id + rev = True + op1, op2 = op2, op1 + else: + rev = False + + output = _8bit_oper(op1, op2, rev) + output.append('sub h') + output.append('push af') + + return output + + +def _mul8(ins): + """ Multiplies 2 las values from the stack. + + Optimizations: + * If any of the ops is ZERO, + then do A = 0 ==> XOR A, cause A * 0 = 0 * A = 0 + + * If any ot the ops is ONE, do NOTHING + A * 1 = 1 * A = A + """ + + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) + if op2 == 1: # A * 1 = 1 * A = A + output.append('push af') + return output + + if op2 == 0: + output.append('xor a') + output.append('push af') + return output + + if op2 == 2: # A * 2 == A SLA 1 + output.append('add a, a') + output.append('push af') + return output + + if op2 == 4: # A * 4 == A SLA 2 + output.append('add a, a') + output.append('add a, a') + output.append('push af') + return output + + output.append('ld h, %i' % int8(op2)) + else: + if op2[0] == '_': # stack optimization + op1, op2 = op2, op1 + + output = _8bit_oper(op1, op2) + + output.append('call __MUL8_FAST') # Inmmediate + output.append('push af') + REQUIRES.add('mul8.asm') + return output + + +def _divu8(ins): + """ Divides 2 8bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 1 then + do nothing + + * If 2nd op is 2 then + Shift Right Logical + """ + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op2 = int8(op2) + + output = _8bit_oper(op1) + if op2 == 1: + output.append('push af') + return output + + if op2 == 2: + output.append('srl a') + output.append('push af') + return output + + output.append('ld h, %i' % int8(op2)) + else: + if op2[0] == '_': # Optimization when 2nd operand is an id + if is_int(op1) and int(op1) == 0: + output = list() # Optimization: Discard previous op if not from the stack + output.append('xor a') + output.append('push af') + return output + + rev = True + op1, op2 = op2, op1 + else: + rev = False + + output = _8bit_oper(op1, op2, rev) + + output.append('call __DIVU8_FAST') + output.append('push af') + REQUIRES.add('div8.asm') + return output + + +def _divi8(ins): + """ Divides 2 8bit signed integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd op is 1 then + do nothing + + * If 2nd op is 2 then + Shift Right Arithmetic + """ + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op2 = int(op2) & 0xFF + output = _8bit_oper(op1) + + if op2 == 1: + output.append('push af') + return output + + if op2 == -1: + output.append('neg') + output.append('push af') + return output + + if op2 == 2: + output.append('sra a') + output.append('push af') + return output + + output.append('ld h, %i' % int8(op2)) + else: + if op2[0] == '_': # Optimization when 2nd operand is an id + if is_int(op1) and int(op1) == 0: + output = [] # Optimization: Discard previous op if not from the stack + output.append('xor a') + output.append('push af') + return output + + rev = True + op1, op2 = op2, op1 + else: + rev = False + + output = _8bit_oper(op1, op2, rev) + + output.append('call __DIVI8_FAST') + output.append('push af') + REQUIRES.add('div8.asm') + return output + + +def _modu8(ins): + """ Reminder of div. 2 8bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd operands is 1 then + returns 0 + + * If 2nd operand = 2^n => do AND (2^n - 1) + + """ + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op2 = int8(op2) + + output = _8bit_oper(op1) + if op2 == 1: + if op1[0] == '_': + output = [] # Optimization: Discard previous op if not from the stack + + output.append('xor a') + output.append('push af') + return output + + if is_2n(op2): + output.append('and %i' % (op2 - 1)) + output.append('push af') + return output + + output.append('ld h, %i' % int8(op2)) + else: + if op2[0] == '_': # Optimization when 2nd operand is an id + if is_int(op1) and int(op1) == 0: + output = [] # Optimization: Discard previous op if not from the stack + output.append('xor a') + output.append('push af') + return output + + rev = True + op1, op2 = op2, op1 + else: + rev = False + + output = _8bit_oper(op1, op2, rev) + + output.append('call __MODU8_FAST') + output.append('push af') + REQUIRES.add('div8.asm') + return output + + +def _modi8(ins): + """ Reminder of div. 2 8bit unsigned integers. The result is pushed onto the stack. + + Optimizations: + * If 2nd operands is 1 then + returns 0 + + * If 2nd operand = 2^n => do AND (2^n - 1) + + """ + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op2 = int8(op2) + + output = _8bit_oper(op1) + if op2 == 1: + if op1[0] == '_': + output = [] # Optimization: Discard previous op if not from the stack + + output.append('xor a') + output.append('push af') + return output + + if is_2n(op2): + output.append('and %i' % (op2 - 1)) + output.append('push af') + return output + + output.append('ld h, %i' % int8(op2)) + else: + if op2[0] == '_': # Optimization when 2nd operand is an id + if is_int(op1) and int(op1) == 0: + output = [] # Optimization: Discard previous op if not from the stack + output.append('xor a') + output.append('push af') + return output + + rev = True + op1, op2 = op2, op1 + else: + rev = False + + output = _8bit_oper(op1, op2, rev) + + output.append('call __MODI8_FAST') + output.append('push af') + REQUIRES.add('div8.asm') + return output + + +def _ltu8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit unsigned version + """ + output = _8bit_oper(ins.quad[2], ins.quad[3]) + output.append('cp h') + output.append('sbc a, a') + output.append('push af') + + return output + + +def _lti8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit signed version + """ + output = [] + output.extend(_8bit_oper(ins.quad[2], ins.quad[3])) + output.append('call __LTI8') + output.append('push af') + REQUIRES.add('lti8.asm') + + return output + + +def _gtu8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit unsigned version + """ + output = _8bit_oper(ins.quad[2], ins.quad[3], reversed_=True) + output.append('cp h') + output.append('sbc a, a') + output.append('push af') + + return output + + +def _gti8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit signed version + """ + output = _8bit_oper(ins.quad[2], ins.quad[3], reversed_=True) + output.append('call __LTI8') + output.append('push af') + REQUIRES.add('lti8.asm') + + return output + + +def _eq8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand == 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit un/signed version + """ + if is_int(ins.quad[3]): + output = _8bit_oper(ins.quad[2]) + n = int8(ins.quad[3]) + if n: + if n == 1: + output.append('dec a') + else: + output.append('sub %i' % n) + else: + output = _8bit_oper(ins.quad[2], ins.quad[3]) + output.append('sub h') + + output.append('sub 1') # Sets Carry only if 0 + output.append('sbc a, a') + output.append('push af') + + return output + + +def _leu8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit unsigned version + """ + output = _8bit_oper(ins.quad[2], ins.quad[3], reversed_=True) + output.append('sub h') # Carry if H > A + output.append('ccf') # Negates => Carry if H <= A + output.append('sbc a, a') + output.append('push af') + + return output + + +def _lei8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit signed version + """ + output = _8bit_oper(ins.quad[2], ins.quad[3]) + output.append('call __LEI8') + output.append('push af') + REQUIRES.add('lei8.asm') + + return output + + +def _geu8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit unsigned version + """ + if is_int(ins.quad[3]): + output = _8bit_oper(ins.quad[2]) + n = int8(ins.quad[3]) + if n: + output.append('sub %i' % n) + else: + output.append('cp a') + else: + output = _8bit_oper(ins.quad[2], ins.quad[3]) + output.append('sub h') + + output.append('ccf') + output.append('sbc a, a') + output.append('push af') + + return output + + +def _gei8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit signed version + """ + output = _8bit_oper(ins.quad[2], ins.quad[3], reversed_=True) + output.append('call __LEI8') + output.append('push af') + REQUIRES.add('lei8.asm') + + return output + + +def _ne8(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand != 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + 8 bit un/signed version + """ + if is_int(ins.quad[3]): + output = _8bit_oper(ins.quad[2]) + n = int8(ins.quad[3]) + if n: + if n == 1: + output.append('dec a') + else: + output.append('sub %i' % int8(ins.quad[3])) + else: + output = _8bit_oper(ins.quad[2], ins.quad[3]) + output.append('sub h') + + output.append('push af') + + return output + + +def _or8(ins): + """ Pops top 2 operands out of the stack, and checks + if 1st operand OR (logical) 2nd operand (top of the stack), + pushes 0 if False, not 0 if True. + + 8 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) + if op2 == 0: # X or False = X + output.append('push af') + return output + + # X or True = True + output.append('ld a, 1') # True + output.append('push af') + return output + + output = _8bit_oper(op1, op2) + output.append('or h') + output.append('push af') + + return output + + +def _bor8(ins): + """ pops top 2 operands out of the stack, and does + OR (bitwise) with 1st and 2nd operand (top of the stack), + pushes result. + + 8 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) + if op2 == 0: # X | 0 = X + output.append('push af') + return output + + if op2 == 0xFF: # X | 0xFF = 0xFF + output.append('ld a, 0FFh') + output.append('push af') + return output + + op1, op2 = tuple(ins.quad[2:]) + + output = _8bit_oper(op1, op2) + output.append('or h') + output.append('push af') + + return output + + +def _and8(ins): + """ Pops top 2 operands out of the stack, and checks + if 1st operand AND (logical) 2nd operand (top of the stack), + pushes 0 if False, not 0 if True. + + 8 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) # Pops the stack (if applicable) + if op2 != 0: # X and True = X + output.append('push af') + return output + + # False and X = False + output.append('xor a') + output.append('push af') + return output + + output = _8bit_oper(op1, op2) + # output.append('call __AND8') + lbl = tmp_label() + output.append('or a') + output.append('jr z, %s' % lbl) + output.append('ld a, h') + output.append('%s:' % lbl) + output.append('push af') + # REQUIRES.add('and8.asm') + + return output + + +def _band8(ins): + """ Pops top 2 operands out of the stack, and does + 1st AND (bitwise) 2nd operand (top of the stack), + pushes the result. + + 8 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) + if op2 == 0xFF: # X & 0xFF = X + output.append('push af') + return output + + if op2 == 0: # X and 0 = 0 + output.append('xor a') + output.append('push af') + return output + + op1, op2 = tuple(ins.quad[2:]) + + output = _8bit_oper(op1, op2) + output.append('and h') + output.append('push af') + + return output + + +def _xor8(ins): + """ Pops top 2 operands out of the stack, and checks + if 1st operand XOR (logical) 2nd operand (top of the stack), + pushes 0 if False, 1 if True. + + 8 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) # True or X = not X + if op2 == 0: # False xor X = X + output.append('push af') + return output + + output.append('sub 1') + output.append('sbc a, a') + output.append('push af') + return output + + output = _8bit_oper(op1, op2) + output.append('call __XOR8') + output.append('push af') + REQUIRES.add('xor8.asm') + + return output + + +def _bxor8(ins): + """ Pops top 2 operands out of the stack, and does + 1st operand XOR (bitwise) 2nd operand (top of the stack), + pushes the result + + 8 bit un/signed version + """ + op1, op2 = tuple(ins.quad[2:]) + if _int_ops(op1, op2) is not None: + op1, op2 = _int_ops(op1, op2) + + output = _8bit_oper(op1) + if op2 == 0: # 0 xor X = X + output.append('push af') + return output + + if op2 == 0xFF: # X xor 0xFF = ~X + output.append('cpl') + output.append('push af') + return output + + op1, op2 = tuple(ins.quad[2:]) + + output = _8bit_oper(op1, op2) + output.append('xor h') + output.append('push af') + + return output + + +def _not8(ins): + """ Negates (Logical NOT) top of the stack (8 bits in AF) + """ + output = _8bit_oper(ins.quad[2]) + output.append('sub 1') # Gives carry only if A = 0 + output.append('sbc a, a') # Gives FF only if Carry else 0 + output.append('push af') + + return output + + +def _bnot8(ins): + """ Negates (BITWISE NOT) top of the stack (8 bits in AF) + """ + output = _8bit_oper(ins.quad[2]) + output.append('cpl') # Gives carry only if A = 0 + output.append('push af') + + return output + + +def _neg8(ins): + """ Negates top of the stack (8 bits in AF) + """ + output = _8bit_oper(ins.quad[2]) + output.append('neg') + output.append('push af') + + return output + + +def _abs8(ins): + """ Absolute value of top of the stack (8 bits in AF) + """ + output = _8bit_oper(ins.quad[2]) + output.append('call __ABS8') + output.append('push af') + REQUIRES.add('abs8.asm') + return output + + +def _shru8(ins): + """ Shift 8bit unsigned integer to the right. The result is pushed onto the stack. + + Optimizations: + * If 1nd or 2nd op is 0 then + do nothing + + * If 2nd op is < 4 then + unroll loop + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + op2 = int8(op2) + + output = _8bit_oper(op1) + if op2 == 0: + output.append('push af') + return output + + if op2 < 4: + output.extend(['srl a'] * op2) + output.append('push af') + return output + + label = tmp_label() + output.append('ld b, %i' % int8(op2)) + output.append('%s:' % label) + output.append('srl a') + output.append('djnz %s' % label) + output.append('push af') + return output + + if is_int(op1) and int(op1) == 0: + output = _8bit_oper(op2) + output.append('xor a') + output.append('push af') + return output + + output = _8bit_oper(op1, op2, True) + label = tmp_label() + label2 = tmp_label() + output.append('or a') + output.append('ld b, a') + output.append('ld a, h') + output.append('jr z, %s' % label2) + output.append('%s:' % label) + output.append('srl a') + output.append('djnz %s' % label) + output.append('%s:' % label2) + output.append('push af') + return output + + +def _shri8(ins): + """ Shift 8bit signed integer to the right. The result is pushed onto the stack. + + Optimizations: + * If 1nd or 2nd op is 0 then + do nothing + + * If 2nd op is < 4 then + unroll loop + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_int(op2): + op2 = int8(op2) + + output = _8bit_oper(op1) + if op2 == 0: + output.append('push af') + return output + + if op2 < 4: + output.extend(['sra a'] * op2) + output.append('push af') + return output + + label = tmp_label() + output.append('ld b, %i' % int8(op2)) + output.append('%s:' % label) + output.append('sra a') + output.append('djnz %s' % label) + output.append('push af') + return output + + if is_int(op1) and int(op1) == 0: + output = _8bit_oper(op2) + output.append('xor a') + output.append('push af') + return output + + output = _8bit_oper(op1, op2, True) + label = tmp_label() + label2 = tmp_label() + output.append('or a') + output.append('ld b, a') + output.append('ld a, h') + output.append('jr z, %s' % label2) + output.append('%s:' % label) + output.append('sra a') + output.append('djnz %s' % label) + output.append('%s:' % label2) + output.append('push af') + return output + + +def _shl8(ins): + """ Shift 8bit (un)signed integer to the left. The result is pushed onto the stack. + + Optimizations: + * If 1nd or 2nd op is 0 then + do nothing + + * If 2nd op is < 4 then + unroll loop + """ + op1, op2 = tuple(ins.quad[2:]) + if is_int(op2): + op2 = int8(op2) + + output = _8bit_oper(op1) + if op2 == 0: + output.append('push af') + return output + + if op2 < 6: + output.extend(['add a, a'] * op2) + output.append('push af') + return output + + label = tmp_label() + output.append('ld b, %i' % int8(op2)) + output.append('%s:' % label) + output.append('add a, a') + output.append('djnz %s' % label) + output.append('push af') + return output + + if is_int(op1) and int(op1) == 0: + output = _8bit_oper(op2) + output.append('xor a') + output.append('push af') + return output + + output = _8bit_oper(op1, op2, True) + label = tmp_label() + label2 = tmp_label() + output.append('or a') + output.append('ld b, a') + output.append('ld a, h') + output.append('jr z, %s' % label2) + output.append('%s:' % label) + output.append('add a, a') + output.append('djnz %s' % label) + output.append('%s:' % label2) + output.append('push af') + return output diff --git a/arch/zxnext/backend/__array.py b/arch/zxnext/backend/__array.py new file mode 100644 index 000000000..c29ffcff2 --- /dev/null +++ b/arch/zxnext/backend/__array.py @@ -0,0 +1,398 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim:ts=4:et:sw=4: + +# -------------------------------------------------------------- +# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa +# (a.k.a. Boriel, http://www.boriel.com) +# +# This module contains array load/store +# intermediate-code traductions +# -------------------------------------------------------------- + + +from .__common import REQUIRES, is_int +from .__f16 import _f16_oper +from .__float import _fpush, _float_oper +from .errors import InvalidICError + + +def _addr(value): + ''' Common subroutine for emitting array address + ''' + output = [] + + try: + indirect = False + if value[0] == '*': + indirect = True + value = value[1:] + + value = int(value) & 0xFFFF + if indirect: + output.append('ld hl, (%s)' % str(value)) + else: + output.append('ld hl, %s' % str(value)) + + except ValueError: + if value[0] == '_': + output.append('ld hl, %s' % str(value)) + if indirect: + output.append('ld c, (hl)') + output.append('inc hl') + output.append('ld h, (hl)') + output.append('ld l, c') + else: + output.append('pop hl') + if indirect: + output.append('ld c, (hl)') + output.append('inc hl') + output.append('ld h, (hl)') + output.append('ld l, c') + + output.append('call __ARRAY') + REQUIRES.add('array.asm') + + return output + + +def _aaddr(ins): + ''' Loads the address of an array element + into the stack. + ''' + output = _addr(ins.quad[2]) + output.append('push hl') + + return output + + +def _aload8(ins): + ''' Loads an 8 bit value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + ''' + output = _addr(ins.quad[2]) + output.append('ld a, (hl)') + output.append('push af') + + return output + + +def _aload16(ins): + ''' Loads a 16 bit value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + ''' + output = _addr(ins.quad[2]) + + output.append('ld e, (hl)') + output.append('inc hl') + output.append('ld d, (hl)') + output.append('ex de, hl') + output.append('push hl') + + return output + + +def _aload32(ins): + ''' Load a 32 bit value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + ''' + output = _addr(ins.quad[2]) + + output.append('call __ILOAD32') + output.append('push de') + output.append('push hl') + + REQUIRES.add('iload32.asm') + + return output + + +def _aloadf(ins): + ''' Loads a floating point value from a memory address. + If 2nd arg. start with '*', it is always treated as + an indirect value. + ''' + output = _addr(ins.quad[2]) + output.append('call __LOADF') + output.extend(_fpush()) + + REQUIRES.add('iloadf.asm') + + return output + + +def _aloadstr(ins): + ''' Loads a string value from a memory address. + ''' + output = _addr(ins.quad[2]) + + output.append('call __ILOADSTR') + output.append('push hl') + REQUIRES.add('loadstr.asm') + + return output + + +def _astore8(ins): + ''' Stores 2º operand content into address of 1st operand. + 1st operand is an array element. Dimensions are pushed into the + stack. + Use '*' for indirect store on 1st operand (A pointer to an array) + ''' + output = _addr(ins.quad[1]) + op = ins.quad[2] + + indirect = op[0] == '*' + if indirect: + op = op[1:] + + immediate = op[0] == '#' + if immediate: + op = op[1:] + + if is_int(op): + if indirect: + if immediate: + op = str(int(op) & 0xFFFF) # Truncate to 16bit pointer + output.append('ld a, (%s)' % op) + else: + output.append('ld de, (%s)' % op) + output.append('ld a, (de)') + else: + op = str(int(op) & 0xFF) # Truncate to byte + output.append('ld (hl), %s' % op) + return output + + elif op[0] == '_': + if indirect: + if immediate: + output.append('ld a, (%s)' % op) # Redundant: *#_id == _id + else: + output.append('ld de, (%s)' % op) # *_id + output.append('ld a, (de)') + else: + if immediate: + output.append('ld a, %s' % op) # #_id + else: + output.append('ld a, (%s)' % op) # _id + else: + output.append('pop af') # tn + + output.append('ld (hl), a') + + return output + + +def _astore16(ins): + ''' Stores 2º operand content into address of 1st operand. + store16 a, x => *(&a) = x + Use '*' for indirect store on 1st operand. + ''' + output = _addr(ins.quad[1]) + op = ins.quad[2] + + indirect = op[0] == '*' + if indirect: + op = op[1:] + + immediate = op[0] == '#' + if immediate: + op = op[1:] + + if is_int(op): + op = str(int(op) & 0xFFFF) # Truncate to 16bit pointer + + if indirect: + if immediate: + output.append('ld de, (%s)' % op) + else: + output.append('ld de, (%s)' % op) + output.append('call __LOAD_DE_DE') + REQUIRES.add('lddede.asm') + else: + H = int(op) >> 8 + L = int(op) & 0xFF + output.append('ld (hl), %i' % L) + output.append('inc hl') + output.append('ld (hl), %i' % H) + return output + + elif op[0] == '_': + if indirect: + if immediate: + output.append('ld de, (%s)' % op) # redundant: *#_id == _id + else: + output.append('ld de, (%s)' % op) # *_id + output.append('call __LOAD_DE_DE') + REQUIRES.add('lddede.asm') + else: + if immediate: + output.append('ld de, %s' % op) + else: + output.append('ld de, (%s)' % op) + else: + output.append('pop de') + + output.append('ld (hl), e') + output.append('inc hl') + output.append('ld (hl), d') + + return output + + +def _astore32(ins): + ''' Stores 2º operand content into address of 1st operand. + store16 a, x => *(&a) = x + ''' + output = _addr(ins.quad[1]) + + value = ins.quad[2] + if value[0] == '*': + value = value[1:] + indirect = True + else: + indirect = False + + try: + value = int(ins.quad[2]) & 0xFFFFFFFF # Immediate? + if indirect: + output.append('push hl') + output.append('ld hl, %i' % (value & 0xFFFF)) + output.append('call __ILOAD32') + output.append('ld b, h') + output.append('ld c, l') # BC = Lower 16 bits + output.append('pop hl') + REQUIRES.add('iload32.asm') + else: + output.append('ld de, %i' % (value >> 16)) + output.append('ld bc, %i' % (value & 0xFFFF)) + except ValueError: + output.append('pop bc') + output.append('pop de') + + output.append('call __STORE32') + REQUIRES.add('store32.asm') + + return output + + +def _astoref16(ins): + ''' Stores 2º operand content into address of 1st operand. + storef16 a, x => *(&a) = x + ''' + output = _addr(ins.quad[1]) + + value = ins.quad[2] + if value[0] == '*': + value = value[1:] + indirect = True + else: + indirect = False + + if indirect: + output.append('push hl') + output.extend(_f16_oper(ins.quad[2], useBC=True)) + output.append('pop hl') + REQUIRES.add('iload32.asm') + else: + output.extend(_f16_oper(ins.quad[2], useBC=True)) + + output.append('call __STORE32') + REQUIRES.add('store32.asm') + + return output + + +def _astoref(ins): + ''' Stores a floating point value into a memory address. + ''' + output = _addr(ins.quad[1]) + + value = ins.quad[2] + if value[0] == '*': + value = value[1:] + indirect = True + else: + indirect = False + + if indirect: + output.append('push hl') + output.extend(_float_oper(ins.quad[2])) + output.append('pop hl') + else: + output.extend(_float_oper(ins.quad[2])) + + output.append('call __STOREF') + REQUIRES.add('storef.asm') + + return output + + +def _astorestr(ins): + ''' Stores a string value into a memory address. + It copies content of 2nd operand (string), into 1st, reallocating + dynamic memory for the 1st str. These instruction DOES ALLOW + immediate strings for the 2nd parameter, starting with '#'. + ''' + output = _addr(ins.quad[1]) + op = ins.quad[2] + + indirect = op[0] == '*' + if indirect: + op = op[1:] + + immediate = op[0] == '#' + if immediate: + op = op[1:] + + temporal = op[0] != '$' + if not temporal: + op = op[1:] + + if is_int(op): + op = str(int(op) & 0xFFFF) + if indirect: + if immediate: # *# = ld hl, (number) + output.append('ld de, (%s)' % op) + else: + output.append('ld de, (%s)' % op) + output.append('call __LOAD_DE_DE') + REQUIRES.add('lddede.asm') + else: + # Integer does not make sense here (unless it's a ptr) + raise InvalidICError(str(ins)) + + output.append('ld de, (%s)' % op) + elif op[0] == '_': # an identifier + temporal = False # Global var is not a temporary string + + if indirect: + if immediate: # *#_id = _id + output.append('ld de, (%s)' % op) + else: # *_id + output.append('ld de, (%s)' % op) + output.append('call __LOAD_DE_DE') + REQUIRES.add('lddede.asm') + else: + if immediate: + output.append('ld de, %s' % op) + else: + output.append('ld de, (%s)' % op) + else: # tn + output.append('pop de') + + if indirect: + output.append('call __LOAD_DE_DE') + REQUIRES.add('lddede.asm') + + if not temporal: + output.append('call __STORE_STR') + REQUIRES.add('storestr.asm') + else: # A value already on dynamic memory + output.append('call __STORE_STR2') + REQUIRES.add('storestr2.asm') + + return output diff --git a/arch/zxnext/backend/__common.py b/arch/zxnext/backend/__common.py new file mode 100644 index 000000000..21dc518e1 --- /dev/null +++ b/arch/zxnext/backend/__common.py @@ -0,0 +1,165 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim ts=4:et:sw=4:ai + +import math +from .errors import TempAlreadyFreedError + +MEMORY = [] # Must be initialized by with init() + +# Counter for generated labels (__LABEL0, __LABEL1, __LABELN...) +LABEL_COUNTER = 0 + +# Counter for generated tmp labels (__TMP0, __TMP1, __TMPN) +TMP_COUNTER = 0 +TMP_STORAGES = [] + +# Set containing REQUIRED libraries +REQUIRES = set() # Set of required libraries (included once) + +# Set containing automatic on start called routines +INITS = set() # Set of INIT routines + +# CONSTANT LN(2) +__LN2 = math.log(2) + +# GENERATED labels __LABELXX +TMP_LABELS = set() + + +def init(): + global LABEL_COUNTER + global TMP_COUNTER + + LABEL_COUNTER = 0 + TMP_COUNTER = 0 + + del MEMORY[:] + del TMP_STORAGES[:] + REQUIRES.clear() + INITS.clear() + TMP_LABELS.clear() + + +def log2(x) -> float: + """ Returns log2(x) + """ + return math.log(x) / __LN2 + + +def is_2n(x) -> bool: + """ Returns true if x is an exact + power of 2 + """ + l = log2(x) + return l == int(l) + + +def tmp_label() -> str: + global LABEL_COUNTER + global TMP_LABELS + + result = '__LABEL%i' % LABEL_COUNTER + TMP_LABELS.add(result) + LABEL_COUNTER += 1 + + return result + + +def tmp_temp() -> str: + global TMP_COUNTER + + for i in range(TMP_COUNTER): + result = '__TEMP%i' % i + + if result not in TMP_STORAGES: + TMP_STORAGES.append(result) + return result + + result = '__TEMP%i' % TMP_COUNTER + TMP_STORAGES.append(result) + TMP_COUNTER += 1 + + return result + + +def tmp_remove(label): + if label not in TMP_STORAGES: + raise TempAlreadyFreedError(label) + + TMP_STORAGES.pop(TMP_STORAGES.index(label)) + + +# ------------------------------------------------------------------ +# Operands checking +# ------------------------------------------------------------------ +def is_int(op): + """ Returns True if the given operand (string) + contains an integer number + """ + try: + int(op) + return True + + except ValueError: + pass + + return False + + +def is_float(op): + """ Returns True if the given operand (string) + contains a floating point number + """ + try: + float(op) + return True + + except ValueError: + pass + + return False + + +def _int_ops(op1, op2, swap=True): + """ Receives a list with two strings (operands). + If none of them contains integers, returns None. + Otherwise, returns a t-uple with (op[0], op[1]), + where op[1] is the integer one (the list is swapped) + unless swap is False (e.g. sub and div used this + because they're not commutative). + + The integer operand is always converted to int type. + """ + if is_int(op1): + if swap: + return op2, int(op1) + else: + return int(op1), op2 + + if is_int(op2): + return op1, int(op2) + + return None + + +def _f_ops(op1, op2, swap=True): + """ Receives a list with two strings (operands). + If none of them contains integers, returns None. + Otherwise, returns a t-uple with (op[0], op[1]), + where op[1] is the integer one (the list is swapped) + unless swap is False (e.g. sub and div used this + because they're not commutative). + + The integer operand is always converted to int type. + """ + if is_float(op1): + if swap: + return op2, float(op1) + else: + return float(op1), op2 + + if is_float(op2): + return op1, float(op2) + + return None diff --git a/arch/zxnext/backend/__f16.py b/arch/zxnext/backend/__f16.py new file mode 100644 index 000000000..668c6f0ce --- /dev/null +++ b/arch/zxnext/backend/__f16.py @@ -0,0 +1,434 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# -------------------------------------------------------------- +# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa +# (a.k.a. Boriel, http://www.boriel.com) +# +# This module contains 8 bit boolean, arithmetic and +# comparation intermediate-code traductions +# -------------------------------------------------------------- + +from .__common import REQUIRES, is_float, _f_ops +from .__32bit import _add32, _sub32, _lti32, _gti32, _gei32, _lei32, _ne32, _eq32 +from .__32bit import _and32, _xor32, _or32, _not32, _neg32, _abs32 +from .__float import _negf + + +# ----------------------------------------------------- +# Fixed Point (16.16) bits operands +# ----------------------------------------------------- +def f16(op): + """ Returns a floating point operand converted to 32 bits unsigned int. + Negative numbers are returned in 2 complement. + + The result is returned in a tuple (DE, HL) => High16 (Int part), Low16 (Decimal part) + """ + op = float(op) + + negative = op < 0 + if negative: + op = -op + + DE = int(op) + HL = int((op - DE) * 2**16) & 0xFFFF + DE &= 0xFFFF + + if negative: # Do C2 + DE ^= 0xFFFF + HL ^= 0xFFFF + + DEHL = ((DE << 16) | HL) + 1 + HL = DEHL & 0xFFFF + DE = (DEHL >> 16) & 0xFFFF + + return (DE, HL) + + +def _f16_oper(op1, op2=None, useBC=False, reversed=False): + """ Returns pop sequence for 32 bits operands + 1st operand in HLDE, 2nd operand remains in the stack + + Now it does support operands inversion calling __SWAP32. + + However, if 1st operand is integer (immediate) or indirect, the stack + will be rearranged, so it contains a 32 bit pushed parameter value for the + subroutine to be called. + + If preserveHL is True, then BC will be used instead of HL for lower part + for the 1st operand. + """ + output = [] + + if op1 is not None: + op1 = str(op1) + + if op2 is not None: + op2 = str(op2) + + op = op2 if op2 is not None else op1 + + float1 = False # whether op1 (2nd operand) is float + + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + hl = 'hl' if not useBC and not indirect else 'bc' + + if is_float(op): + float1 = True + op = float(op) + + if indirect: + op = int(op) & 0xFFFF + if immediate: + output.append('ld hl, %i' % op) + else: + output.append('ld hl, (%i)' % op) + + output.append('call __ILOAD32') + REQUIRES.add('iload32.asm') + + if preserveHL: # noqa TODO: it will fail + output.append('ld b, h') + output.append('ld c, l') + else: + DE, HL = f16(op) + output.append('ld de, %i' % DE) + output.append('ld %s, %i' % (hl, HL)) + else: + if op[0] == '_': + if immediate: + output.append('ld %s, %s' % (hl, op)) + else: + output.append('ld %s, (%s)' % (hl, op)) + else: + output.append('pop %s' % hl) + + if indirect: + output.append('call __ILOAD32') + REQUIRES.add('iload32.asm') + + if preserveHL: # noqa TODO: it will fail + output.append('ld b, h') + output.append('ld c, l') + else: + if op[0] == '_': + output.append('ld de, (%s + 2)' % op) + else: + output.append('pop de') + + if op2 is not None: + op = op1 + + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + immediate = (op[0] == '#') + if immediate: + op = op[1:] + + if is_float(op): + op = float(op) + + if indirect: + op = int(op) + output.append('exx') + if immediate: + output.append('ld hl, %i' % (op & 0xFFFF)) + else: + output.append('ld hl, (%i)' % (op & 0xFFFF)) + + output.append('call __ILOAD32') + output.append('push de') + output.append('push hl') + output.append('exx') + REQUIRES.add('iload32.asm') + else: + DE, HL = f16(op) + output.append('ld bc, %i' % DE) + output.append('push bc') + output.append('ld bc, %i' % HL) + output.append('push bc') + else: + if indirect: + output.append('exx') # uses alternate set to put it on the stack + if op[0] == '_': + if immediate: + output.append('ld hl, %s' % op) + else: + output.append('ld hl, (%s)' % op) + else: + output.append('pop hl') # Pointers are only 16 bits *** + + output.append('call __ILOAD32') + output.append('push de') + output.append('push hl') + output.append('exx') + REQUIRES.add('iload32.asm') + elif op[0] == '_': # an address + if float1 or op1[0] == '_': # If previous op was constant, we can use hl in advance + tmp = output + output = [] + output.append('ld hl, (%s + 2)' % op) + output.append('push hl') + output.append('ld hl, (%s)' % op) + output.append('push hl') + output.extend(tmp) + else: + output.append('ld bc, (%s + 2)' % op) + output.append('push bc') + output.append('ld bc, (%s)' % op) + output.append('push bc') + else: + pass # 2nd operand remains in the stack + + if op2 is not None and reversed: + output.append('call __SWAP32') + REQUIRES.add('swap32.asm') + + return output + + +def _f16_to_32bit(ins): + """ If any of the operands within the ins(truction) are numeric, + convert them to its 32bit representation, otherwise leave them + as they are. + """ + ins.quad = [x for x in ins.quad] + for i in range(2, len(ins.quad)): + if is_float(ins.quad[i]): + de, hl = f16(ins.quad[i]) + ins.quad[i] = str((de << 16) | hl) + + ins.quad = tuple(ins.quad) + return ins + + +def _addf16(ins): + """ Pops last 2 bytes from the stack and adds them. + Then push the result onto the stack. + + Optimizations: + * If any of the operands is ZERO, + then do NOTHING: A + 0 = 0 + A = A + """ + return _add32(_f16_to_32bit(ins)) + + +def _subf16(ins): + """ Pops last 2 dwords from the stack and subtract them. + Then push the result onto the stack. + NOTE: The operation is TOP[0] = TOP[-1] - TOP[0] + + If TOP[0] is 0, nothing is done + """ + return _sub32(_f16_to_32bit(ins)) + + +def _mulf16(ins): + """ Multiplies 2 32bit (16.16) fixed point numbers. The result is pushed onto the stack. + """ + op1, op2 = tuple(ins.quad[2:]) + + if _f_ops(op1, op2) is not None: + op1, op2 = _f_ops(op1, op2) + + if op2 == 1: # A * 1 => A + output = _f16_oper(op1) + output.append('push de') + output.append('push hl') + return output + + if op2 == -1: + return _neg32(ins) + + output = _f16_oper(op1) + if op2 == 0: + output.append('ld hl, 0') + output.append('ld e, h') + output.append('ld d, l') + output.append('push de') + output.append('push hl') + return output + + output = _f16_oper(op1, str(op2)) + output.append('call __MULF16') + output.append('push de') + output.append('push hl') + REQUIRES.add('mulf16.asm') + return output + + +def _divf16(ins): + """ Divides 2 32bit (16.16) fixed point numbers. The result is pushed onto the stack. + + Optimizations: + + * If 2nd operand is 1, do nothing + * If 2nd operand is -1, do NEG32 + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_float(op2): + if float(op2) == 1: + output = _f16_oper(op1) + output.append('push de') + output.append('push hl') + return output + + if float(op2) == -1: + return _negf(ins) + + rev = not is_float(op1) and op1[0] != 't' and op2[0] == 't' + + output = _f16_oper(op1, op2, reversed=rev) + output.append('call __DIVF16') + output.append('push de') + output.append('push hl') + REQUIRES.add('divf16.asm') + return output + + +def _modf16(ins): + """ Reminder of div. 2 32bit (16.16) fixed point numbers. The result is pushed onto the stack. + Optimizations: + * If 2nd op is 1. Returns 0 + """ + op1, op2 = tuple(ins.quad[2:]) + + if is_float(op2) and float(op2) == 1: + output = _f16_oper(op1) + output.append('ld hl, 0') + output.append('push hl') + output.append('push hl') + return output + + rev = not is_float(op1) and op1[0] != 't' and op2[0] == 't' + + output = _f16_oper(op1, op2, reversed=rev) + output.append('call __MODF16') + output.append('push de') + output.append('push hl') + REQUIRES.add('modf16.asm') + return output + + +def _negf16(ins): + """ Negates (arithmetic) top of the stack (Fixed point in DE.HL) + + Fixed point signed version + """ + return _neg32(_f16_to_32bit(ins)) + + +def _ltf16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _lti32(_f16_to_32bit(ins)) + + +def _gtf16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _gti32(_f16_to_32bit(ins)) + + +def _lef16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _lei32(_f16_to_32bit(ins)) + + +def _gef16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _gei32(_f16_to_32bit(ins)) + + +def _eqf16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand == 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _eq32(_f16_to_32bit(ins)) + + +def _nef16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand != 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _ne32(_f16_to_32bit(ins)) + + +def _orf16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand OR (Logical) 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _or32(_f16_to_32bit(ins)) + + +def _xorf16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand XOR (Logical) 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _xor32(_f16_to_32bit(ins)) + + +def _andf16(ins): + """ Compares & pops top 2 operands out of the stack, and checks + if the 1st operand AND (Logical) 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Fixed point signed version + """ + return _and32(_f16_to_32bit(ins)) + + +def _notf16(ins): + """ Negates top of the stack (Fixed point in DE.HL) + + Fixed point signed version + """ + return _not32(_f16_to_32bit(ins)) + + +def _absf16(ins): + """ Absolute value of top of the stack (Fixed point in DE.HL) + + Fixed point signed version + """ + return _abs32(_f16_to_32bit(ins)) diff --git a/arch/zxnext/backend/__float.py b/arch/zxnext/backend/__float.py new file mode 100644 index 000000000..c23389b59 --- /dev/null +++ b/arch/zxnext/backend/__float.py @@ -0,0 +1,425 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vim:ts=4:et:sw=4: + +# -------------------------------------------------------------- +# Copyleft (k) 2008, by Jose M. Rodriguez-Rosa +# (a.k.a. Boriel, http://www.boriel.com) +# +# This module contains 8 bit boolean, arithmetic and +# comparation intermediate-code traductions +# -------------------------------------------------------------- + +from .__common import REQUIRES, is_float, _f_ops + +# ----------------------------------------------------- +# Floating Point operators +# ----------------------------------------------------- +from api import fp + + +def _float(op): + ''' Returns a floating point operand converted to 5 byte (40 bits) unsigned int. + The result is returned in a tuple (C, DE, HL) => Exp, mantissa =>High16 (Int part), Low16 (Decimal part) + ''' + return fp.immediate_float(float(op)) + + +def _fpop(): + ''' Returns the pop sequence of a float + ''' + output = [] + output.append('pop af') + output.append('pop de') + output.append('pop bc') + + return output + + +def _fpush(): + ''' Returns the push sequence of a float + ''' + output = [] + output.append('push bc') + output.append('push de') + output.append('push af') + + return output + + +def _float_oper(op1, op2=None): + ''' Returns pop sequence for floating point operands + 1st operand in A DE BC, 2nd operand remains in the stack + + Unlike 8bit and 16bit version, this does not supports + operands inversion. Since many of the instructions are implemented + as functions, they must support this. + + However, if 1st operand is a number (immediate) or indirect, the stack + will be rearranged, so it contains a 48 bit pushed parameter value for the + subroutine to be called. + ''' + output = [] + op = op2 if op2 is not None else op1 + + indirect = (op[0] == '*') + if indirect: + op = op[1:] + + if is_float(op): + op = float(op) + + if indirect: + op = int(op) & 0xFFFF + output.append('ld hl, (%i)' % op) + output.append('call __ILOADF') + REQUIRES.add('iloadf.asm') + else: + A, DE, BC = _float(op) + output.append('ld a, %s' % A) + output.append('ld de, %s' % DE) + output.append('ld bc, %s' % BC) + else: + if indirect: + if op[0] == '_': + output.append('ld hl, (%s)' % op) + else: + output.append('pop hl') + + output.append('call __ILOADF') + REQUIRES.add('iloadf.asm') + else: + if op[0] == '_': + output.append('ld a, (%s)' % op) + output.append('ld de, (%s + 1)' % op) + output.append('ld bc, (%s + 3)' % op) + else: + output.extend(_fpop()) + + if op2 is not None: + op = op1 + if is_float(op): # An float must be in the stack. Let's pushit + A, DE, BC = _float(op) + output.append('ld hl, %s' % BC) + output.append('push hl') + output.append('ld hl, %s' % DE) + output.append('push hl') + output.append('ld h, %s' % A) + output.append('push hl') + elif op[0] == '*': # Indirect + op = op[1:] + output.append('exx') # uses alternate set to put it on the stack + output.append("ex af, af'") + if is_int(op): # noqa TODO: it will fail + op = int(op) + output.append('ld hl, %i' % op) + elif op[0] == '_': + output.append('ld hl, (%s)' % op) + else: + output.append('pop hl') + + output.append('call __ILOADF') + output.extend(_fpush()) + output.append("ex af, af'") + output.append('exx') + REQUIRES.add('iloadf.asm') + elif op[0] == '_': + if is_float(op2): + tmp = output + output = [] + output.append('ld hl, %s + 4' % op) + ''' + output.append('ld hl, (%s + 3)' % op) + output.append('push hl') + output.append('ld hl, (%s + 1)' % op) + output.append('push hl') + output.append('ld a, (%s)' % op) + output.append('push af') + ''' + output.append('call __FP_PUSH_REV') + output.extend(tmp) + REQUIRES.add('pushf.asm') + else: + ''' + output.append('ld hl, (%s + 3)' % op) + output.append('push hl') + output.append('ld hl, (%s + 1)' % op) + output.append('push hl') + output.append('ld hl, (%s - 1)' % op) + output.append('push hl') + ''' + output.append('ld hl, %s + 4' % op) + output.append('call __FP_PUSH_REV') + REQUIRES.add('pushf.asm') + else: + pass # Else do nothing, and leave the op onto the stack + + return output + + +# ----------------------------------------------------- +# Arithmetic operations +# ----------------------------------------------------- + +def _addf(ins): + ''' Adds 2 float values. The result is pushed onto the stack. + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _f_ops(op1, op2) is not None: + opa, opb = _f_ops(op1, op2) + if opb == 0: # A + 0 => A + output = _float_oper(opa) + output.extend(_fpush()) + return output + + output = _float_oper(op1, op2) + output.append('call __ADDF') + output.extend(_fpush()) + REQUIRES.add('addf.asm') + return output + + +def _subf(ins): + ''' Subtract 2 float values. The result is pushed onto the stack. + ''' + op1, op2 = tuple(ins.quad[2:]) + + if is_float(op2) and float(op2) == 0: # Nothing to do: A - 0 = A + output = _float_oper(op1) + output.extend(_fpush()) + return output + + output = _float_oper(op1, op2) + output.append('call __SUBF') + output.extend(_fpush()) + REQUIRES.add('subf.asm') + return output + + +def _mulf(ins): + ''' Multiplie 2 float values. The result is pushed onto the stack. + ''' + op1, op2 = tuple(ins.quad[2:]) + + if _f_ops(op1, op2) is not None: + opa, opb = _f_ops(op1, op2) + if opb == 1: # A * 1 => A + output = _float_oper(opa) + output.extend(_fpush()) + return output + + output = _float_oper(op1, op2) + output.append('call __MULF') + output.extend(_fpush()) + REQUIRES.add('mulf.asm') + return output + + +def _divf(ins): + ''' Divides 2 float values. The result is pushed onto the stack. + ''' + op1, op2 = tuple(ins.quad[2:]) + + if is_float(op2) and float(op2) == 1: # Nothing to do. A / 1 = A + output = _float_oper(op1) + output.extend(_fpush()) + return output + + output = _float_oper(op1, op2) + output.append('call __DIVF') + output.extend(_fpush()) + REQUIRES.add('divf.asm') + return output + + +def _modf(ins): + ''' Reminder of div. 2 float values. The result is pushed onto the stack. + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __MODF') + output.extend(_fpush()) + REQUIRES.add('modf.asm') + return output + + +def _powf(ins): + ''' Exponentiation of 2 float values. The result is pushed onto the stack. + ''' + op1, op2 = tuple(ins.quad[2:]) + + if is_float(op2) and float(op2) == 1: # Nothing to do. A ^ 1 = A + output = _float_oper(op1) + output.extend(_fpush()) + return output + + output = _float_oper(op1, op2) + output.append('call __POW') + output.extend(_fpush()) + REQUIRES.add('pow.asm') + return output + + +def _ltf(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand < 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __LTF') + output.append('push af') + REQUIRES.add('ltf.asm') + return output + + +def _gtf(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand > 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __GTF') + output.append('push af') + REQUIRES.add('gtf.asm') + return output + + +def _lef(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand <= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __LEF') + output.append('push af') + REQUIRES.add('lef.asm') + return output + + +def _gef(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand >= 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __GEF') + output.append('push af') + REQUIRES.add('gef.asm') + return output + + +def _eqf(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand == 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __EQF') + output.append('push af') + REQUIRES.add('eqf.asm') + return output + + +def _nef(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand != 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __NEF') + output.append('push af') + REQUIRES.add('nef.asm') + return output + + +def _orf(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand || 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __ORF') + output.append('push af') + REQUIRES.add('orf.asm') + return output + + +def _xorf(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand ~~ 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __XORF') + output.append('push af') + REQUIRES.add('xorf.asm') + return output + + +def _andf(ins): + ''' Compares & pops top 2 operands out of the stack, and checks + if the 1st operand && 2nd operand (top of the stack). + Pushes 0 if False, 1 if True. + + Floating Point version + ''' + op1, op2 = tuple(ins.quad[2:]) + output = _float_oper(op1, op2) + output.append('call __ANDF') + output.append('push af') + REQUIRES.add('andf.asm') + return output + + +def _notf(ins): + ''' Negates top of the stack (48 bits) + ''' + output = _float_oper(ins.quad[2]) + output.append('call __NOTF') + output.append('push af') + REQUIRES.add('notf.asm') + return output + + +def _negf(ins): + ''' Changes sign of top of the stack (48 bits) + ''' + output = _float_oper(ins.quad[2]) + output.append('call __NEGF') + output.extend(_fpush()) + REQUIRES.add('negf.asm') + return output + + +def _absf(ins): + ''' Absolute value of top of the stack (48 bits) + ''' + output = _float_oper(ins.quad[2]) + output.append('res 7, e') # Just resets the sign bit! + output.extend(_fpush()) + return output diff --git a/arch/zxnext/backend/__init__.py b/arch/zxnext/backend/__init__.py new file mode 100644 index 000000000..c8d179b79 --- /dev/null +++ b/arch/zxnext/backend/__init__.py @@ -0,0 +1,2363 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim:ts=4:sw=4:et + +import math +import re +from typing import List + +from . import errors +from .errors import InvalidICError as InvalidIC + + +# 8 bit arithmetic functions +from .__8bit import _add8, _sub8, _mul8, _divu8, _divi8, _modu8, _modi8, _neg8, _abs8 +# 8 bit comparison functions +from .__8bit import _eq8, _lti8, _ltu8, _gti8, _gtu8, _ne8, _leu8, _lei8, _geu8, _gei8 +# 8 bit boolean functions +from .__8bit import _or8, _and8, _not8, _xor8, _8bit_oper +# 8 bit shift operations +from .__8bit import _shru8, _shri8, _shl8 +# 8 bit bitwise operations +from .__8bit import _bor8, _band8, _bnot8, _bxor8 + +# 16 bit arithmetic functions +from .__16bit import _add16, _sub16, _mul16, _divu16, _divi16, _modu16, _modi16, _neg16, _abs16 +# 16 bit comparison functions +from .__16bit import _eq16, _lti16, _ltu16, _gti16, _gtu16, _ne16, _leu16, _lei16, _geu16, _gei16 +# 16 bit boolean functions +from .__16bit import _or16, _and16, _not16, _xor16, _16bit_oper +# 16 bit shift operations +from .__16bit import _shru16, _shri16, _shl16 +# 16 bit bitwise operations +from .__16bit import _band16, _bor16, _bxor16, _bnot16 + +# 32 bit arithmetic functions +from .__32bit import _add32, _sub32, _mul32, _divu32, _divi32, _modu32, _modi32, _neg32, _abs32 +# 32 bit comparison functions +from .__32bit import _eq32, _lti32, _ltu32, _gti32, _gtu32, _ne32, _leu32, _lei32, _geu32, _gei32 +# 32 bit boolean functions +from .__32bit import _or32, _and32, _not32, _xor32, _32bit_oper +# 32 bit shift operations +from .__32bit import _shru32, _shri32, _shl32 +# 32 bit bitwise operations +from .__32bit import _band32, _bor32, _bxor32, _bnot32 + +# Fixed Point arithmetic functions +from .__f16 import _addf16, _subf16, _mulf16, _divf16, _modf16, _negf16, _absf16 +# Fixed Point comparison functions +from .__f16 import _eqf16, _ltf16, _gtf16, _nef16, _lef16, _gef16 +# Fixed Point boolean functions +from .__f16 import _orf16, _andf16, _notf16, _xorf16, _f16_oper + +from .__f16 import f16 # Returns DE,HL of a decimal value + +# Floating Point arithmetic functions +from .__float import _addf, _subf, _mulf, _divf, _modf, _negf, _powf, _absf +# Floating Point comparison functions +from .__float import _eqf, _ltf, _gtf, _nef, _lef, _gef +# Floating Point boolean functions +from .__float import _orf, _andf, _notf, _xorf, _float_oper, _fpush, _fpop + +# String arithmetic functions +from .__str import _addstr +# String comparison functions +from .__str import _ltstr, _gtstr, _eqstr, _lestr, _gestr, _nestr, _str_oper, _lenstr + +# Param load and store instructions +from .__pload import _pload8, _pload16, _pload32, _ploadf, _ploadstr, _fploadstr +from .__pload import _pstore8, _pstore16, _pstore32, _pstoref16, _pstoref, _pstorestr +from .__pload import _paddr + +from . import __common +from .__common import MEMORY, LABEL_COUNTER, TMP_LABELS, TMP_COUNTER, TMP_STORAGES, REQUIRES, INITS +from .__common import is_int, is_float, tmp_label + +# Array store and load instructions +from .__array import _aload8, _aload16, _aload32, _aloadf, _aloadstr +from .__array import _astore8, _astore16, _astore32, _astoref16, _astoref, _astorestr +from .__array import _aaddr + +# Array store and load instructions +from .__parray import _paload8, _paload16, _paload32, _paloadf, _paloadstr +from .__parray import _pastore8, _pastore16, _pastore32, _pastoref16, _pastoref, _pastorestr +from .__parray import _paaddr + +# External functions +from ..optimizer.helpers import HI16, LO16 +from arch.zx48k.optimizer.asm import Asm +from api.config import OPTIONS +import api.fp + +from arch.zx48k.peephole import engine + + +__all__ = [ + '_fpop', + 'LABEL_COUNTER', + 'MEMORY', + 'TMP_COUNTER', + 'TMP_STORAGES', + 'HI16', + 'LO16', +] + +# Label RegExp +RE_LABEL = re.compile(r'^[ \t]*[a-zA-Z_][_a-zA-Z\d]*:') + +# (ix +/- ...) regexp +RE_IX_IDX = re.compile(r'^\([ \t]*ix[ \t]*[-+][ \t]*.+\)$') + +# Label for the program START end EXIT +START_LABEL = '__START_PROGRAM' +END_LABEL = '__END_PROGRAM' +CALL_BACK = '__CALL_BACK__' + + +# Whether to use the FunctionExit scheme +FLAG_use_function_exit = False + +# Whether an 'end' has already been emmitted +FLAG_end_emitted = False + +# Default code ORG +OPTIONS.add_option_if_not_defined('org', int, 32768) + +# Default HEAP SIZE (Dynamic memory) in bytes +OPTIONS.add_option_if_not_defined('heap_size', int, 4768) # A bit more than 4K + +# List of modules that, if included, should call MEM_INIT +MEMINITS = ['alloc.asm', 'loadstr.asm', 'storestr2.asm', 'storestr.asm', 'strcpy.asm', + 'string.asm', 'strslice.asm', 'strcat.asm'] + +# Internal data types definition, with its size in bytes, or -1 if it is variable (string) +# Compound types are only arrays, and have the t +YY_TYPES = { + 'u8': 1, # 8 bit unsigned integer + 'u16': 2, # 16 bit unsigned integer + 'u32': 4, # 32 bit unsigned integer + 'i8': 1, # 8 bit SIGNED integer + 'i16': 2, # 16 bit SIGNED integer + 'i32': 4, # 32 bit SIGNED integer + 'f16': 4, # -32768.9999 to 32767.9999 -aprox.- fixed point decimal (step = 1/2^16) + 'f': 5, # Floating point +} + +RE_BOOL = re.compile(r'^(eq|ne|lt|le|gt|ge|and|or|xor|not)(((u|i)(8|16|32))|(f16|f|str))$') +RE_HEXA = re.compile(r'^[0-9A-F]+$') + +# CONSTANT LN(2) +__LN2 = math.log(2) + +# This will be appended at the end (useful for lvard, for example) +AT_END = [] + +# A table with ASM block entered by the USER (these won't be optimized) +ASMS = {} +ASMCOUNT = 0 # ASM blocks counter + + +def init(): + """ Initializes this module + """ + global ASMS + global ASMCOUNT + global AT_END + global FLAG_end_emitted + global FLAG_use_function_exit + + __common.init() + + ASMS = {} + ASMCOUNT = 0 + AT_END = [] + FLAG_use_function_exit = False + FLAG_end_emitted = False + + # Default code ORG + OPTIONS.add_option('org', int, 32768) + # Default HEAP SIZE (Dynamic memory) in bytes + OPTIONS.add_option('heap_size', int, 4768) # A bit more than 4K + # Labels for HEAP START (might not be used if not needed) + OPTIONS.add_option('heap_start_label', str, 'ZXBASIC_MEM_HEAP') + # Labels for HEAP SIZE (might not be used if not needed) + OPTIONS.add_option('heap_size_label', str, 'ZXBASIC_HEAP_SIZE') + # Flag for headerless mode (No prologue / epilogue) + OPTIONS.add_option('headerless', bool, False) + + engine.main() # inits the optimizer + + +def new_ASMID(): + """ Returns a new unique ASM block id + """ + global ASMCOUNT + + result = '##ASM%i' % ASMCOUNT + ASMCOUNT += 1 + return result + + +def log2(x): + """ Returns log2(x) + """ + return math.log(x) / __LN2 + + +def is_2n(x): + """ Returns true if x is an exact + power of 2 + """ + l = log2(x) + return l == int(l) + + +def is_int_type(stype): + """ Returns whether a given type is integer + """ + return stype[0] in ('u', 'i') + + +def get_bytes(elements: List[str]) -> List[str]: + """ Returns a list a default set of bytes/words in hexadecimal + (starting with an hex number) or literals (starting with #). + Numeric values with more than 2 digits represents a WORD (2 bytes) value. + E.g. '01' => 01h, '001' => 1, 0 bytes (0001h) + Literal values starts with # (1 byte) or ## (2 bytes) + E.g. '#label + 1' => (label + 1) & 0xFF + '##(label + 1)' => (label + 1) & 0xFFFF + """ + output = [] + + for x in elements: + if x.startswith('##'): # 2-byte literal + output.append('({}) & 0xFF'.format(x[2:])) + output.append('(({}) >> 8) & 0xFF'.format(x[2:])) + continue + + if x.startswith('#'): # 1-byte literal + output.append('({}) & 0xFF'.format(x[1:])) + continue + + # must be an hex number + assert RE_HEXA.match(x), 'expected an hex number, got "%s"' % x + output.append('%02X' % int(x[-2:], 16)) + if len(x) > 2: + output.append('%02X' % int(x[-4:-2:], 16)) + + return output + + +def get_bytes_size(elements: List[str]) -> int: + """ Defines a memory space with a default set of bytes/words in hexadecimal + (starting with an hex number) or literals (starting with #). + Numeric values with more than 2 digits represents a WORD (2 bytes) value. + E.g. '01' => 01h, '001' => 1, 0 bytes (0001h) + Literal values starts with # (1 byte) or ## (2 bytes) + E.g. '#label + 1' => (label + 1) & 0xFF + '##(label + 1)' => (label + 1) & 0xFFFF + """ + return len(get_bytes(elements)) + + +# ------------------------------------------------------------------ +# Typecast conversions +# ------------------------------------------------------------------ + +def to_byte(stype): + """ Returns the instruction sequence for converting from + the given type to byte. + """ + output = [] + + if stype in ('i8', 'u8'): + return [] + + if is_int_type(stype): + output.append('ld a, l') + elif stype == 'f16': + output.append('ld a, e') + elif stype == 'f': # Converts C ED LH to byte + output.append('call __FTOU32REG') + output.append('ld a, l') + REQUIRES.add('ftou32reg.asm') + + return output + + +def to_word(stype): + """ Returns the instruction sequence for converting the given + type stored in DE,HL to word (unsigned) HL. + """ + output = [] # List of instructions + + if stype == 'u8': # Byte to word + output.append('ld l, a') + output.append('ld h, 0') + + elif stype == 'i8': # Signed byte to word + output.append('ld l, a') + output.append('add a, a') + output.append('sbc a, a') + output.append('ld h, a') + + elif stype == 'f16': # Must MOVE HL into DE + output.append('ex de, hl') + + elif stype == 'f': + output.append('call __FTOU32REG') + REQUIRES.add('ftou32reg.asm') + + return output + + +def to_long(stype): + """ Returns the instruction sequence for converting the given + type stored in DE,HL to long (DE, HL). + """ + output = [] # List of instructions + + if stype in ('i8', 'u8', 'f16'): # Byte to word + output = to_word(stype) + + if stype != 'f16': # If its a byte, just copy H to D,E + output.append('ld e, h') + output.append('ld d, h') + + if stype in ('i16', 'f16'): # Signed byte or fixed to word + output.append('ld a, h') + output.append('add a, a') + output.append('sbc a, a') + output.append('ld e, a') + output.append('ld d, a') + + elif stype == 'u16': + output.append('ld de, 0') + + elif stype == 'f': + output.append('call __FTOU32REG') + REQUIRES.add('ftou32reg.asm') + + return output + + +def to_fixed(stype): + """ Returns the instruction sequence for converting the given + type stored in DE,HL to fixed DE,HL. + """ + output = [] # List of instructions + + if is_int_type(stype): + output = to_word(stype) + output.append('ex de, hl') + output.append('ld hl, 0') # 'Truncate' the fixed point + elif stype == 'f': + output.append('call __FTOF16REG') + REQUIRES.add('ftof16reg.asm') + + return output + + +def to_float(stype): + """ Returns the instruction sequence for converting the given + type stored in DE,HL to fixed DE,HL. + """ + output = [] # List of instructions + + if stype == 'f': + return [] # Nothing to do + + if stype == 'f16': + output.append('call __F16TOFREG') + REQUIRES.add('f16tofreg.asm') + return output + + # If we reach this point, it's an integer type + if stype == 'u8': + output.append('call __U8TOFREG') + elif stype == 'i8': + output.append('call __I8TOFREG') + else: + output = to_long(stype) + if stype in ('i16', 'i32'): + output.append('call __I32TOFREG') + else: + output.append('call __U32TOFREG') + + REQUIRES.add('u32tofreg.asm') + + return output + + +# ------------------------------------------------------------------ +# Lowlevel (to ASM) instructions implementation +# ------------------------------------------------------------------ +def _nop(ins): + """ Returns nothing. (Ignored nop) + """ + return [] + + +def _org(ins): + """ Outputs an origin of code. + """ + return ['org %s' % str(ins.quad[1])] + + +def _exchg(ins): + """ Exchange ALL registers. If the processor + does not support this, it must be implemented saving registers in a memory + location. + """ + output = [] + output.append('ex af, af\'') + output.append('exx') + return output + + +def _end(ins): + """ Outputs the ending sequence + """ + global FLAG_end_emitted + output = _16bit_oper(ins.quad[1]) + output.append('ld b, h') + output.append('ld c, l') + + if FLAG_end_emitted: + return output + ['jp %s' % END_LABEL] + + FLAG_end_emitted = True + + output.append('%s:' % END_LABEL) + if OPTIONS.headerless.value: + return output + ['ret'] + + output.append('di') + output.append('ld hl, (%s)' % CALL_BACK) + output.append('ld sp, hl') + output.append('exx') + output.append('pop hl') + output.append('exx') + output.append('pop iy') + output.append('pop ix') + output.append('ei') + output.append('ret') + output.append('%s:' % CALL_BACK) + output.append('DEFW 0') + + return output + + +def _label(ins): + """ Defines a Label. + """ + return ['%s:' % str(ins.quad[1])] + + +def _deflabel(ins): + """ Defines a Label with a value. + """ + return ['%s EQU %s' % (str(ins.quad[1]), str(ins.quad[2]))] + + +def _data(ins): + """ Defines a data item (binary). + It's just a constant expression to be converted do binary data "as is" + + 1st parameter is the type-size (u8 or i8 for byte, u16 or i16 for word, etc) + 2nd parameter is the list of expressions. All of them will be converted to the + type required. + """ + output = [] + t = ins.quad[1] + q = eval(ins.quad[2]) + + if t in ('i8', 'u8'): + size = 'B' + elif t in ('i16', 'u16'): + size = 'W' + elif t in ('i32', 'u32'): + size = 'W' + z = list() + for expr in ins.quad[2]: + z.extend(['(%s) & 0xFFFF' % expr, '(%s) >> 16' % expr]) + q = z + elif t == 'str': + size = "B" + q = ['"%s"' % x.replace('"', '""') for x in q] + elif t == 'f': + dat_ = [api.fp.immediate_float(float(x)) for x in q] + for x in dat_: + output.extend(['DEFB %s' % x[0], 'DEFW %s, %s' % (x[1], x[2])]) + return output + else: + raise InvalidIC(ins.quad, 'Unimplemented data size %s for %s' % (t, q)) + + for x in q: + output.append('DEF%s %s' % (size, x)) + + return output + + +def _var(ins): + """ Defines a memory variable. + """ + output = [] + output.append('%s:' % ins.quad[1]) + output.append('DEFB %s' % ((int(ins.quad[2]) - 1) * '00, ' + '00')) + + return output + + +def _varx(ins): + """ Defines a memory space with a default CONSTANT expression + 1st parameter is the var name + 2nd parameter is the type-size (u8 or i8 for byte, u16 or i16 for word, etc) + 3rd parameter is the list of expressions. All of them will be converted to the + type required. + """ + output = [] + output.append('%s:' % ins.quad[1]) + q = eval(ins.quad[3]) + + if ins.quad[2] in ('i8', 'u8'): + size = 'B' + elif ins.quad[2] in ('i16', 'u16'): + size = 'W' + elif ins.quad[2] in ('i32', 'u32'): + size = 'W' + z = list() + for expr in q: + z.extend(['(%s) & 0xFFFF' % expr, '(%s) >> 16' % expr]) + q = z + else: + raise InvalidIC(ins.quad, 'Unimplemented vard size: %s' % ins.quad[2]) + + for x in q: + output.append('DEF%s %s' % (size, x)) + + return output + + +def _vard(ins): + """ Defines a memory space with a default set of bytes/words in hexadecimal + (starting with an hex number) or literals (starting with #). + Numeric values with more than 2 digits represents a WORD (2 bytes) value. + E.g. '01' => 01h, '001' => 1, 0 bytes (0001h) + Literal values starts with # (1 byte) or ## (2 bytes) + E.g. '#label + 1' => (label + 1) & 0xFF + '##(label + 1)' => (label + 1) & 0xFFFF + """ + output = [] + output.append('%s:' % ins.quad[1]) + + q = eval(ins.quad[2]) + + for x in q: + if x[0] == '#': # literal? + size_t = 'W' if x[1] == '#' else 'B' + output.append('DEF{0} {1}'.format(size_t, x.lstrip('#'))) + continue + + # must be an hex number + x = x.upper() + assert RE_HEXA.match(x), 'expected an hex number, got "%s"' % x + size_t = 'B' if len(x) <= 2 else 'W' + if x[0] > '9': # Not a number? + x = '0' + x + output.append('DEF{0} {1}h'.format(size_t, x)) + + return output + + +def _lvarx(ins): + """ Defines a local variable. 1st param is offset of the local variable. + 2nd param is the type a list of bytes in hexadecimal. + """ + output = [] + + l = eval(ins.quad[3]) # List of bytes to push + label = tmp_label() + offset = int(ins.quad[1]) + tmp = list(ins.quad) + tmp[1] = label + ins.quad = tmp + AT_END.extend(_varx(ins)) + + output.append('push ix') + output.append('pop hl') + output.append('ld bc, %i' % -offset) + output.append('add hl, bc') + output.append('ex de, hl') + output.append('ld hl, %s' % label) + output.append('ld bc, %i' % (len(l) * YY_TYPES[ins.quad[2]])) + output.append('ldir') + + return output + + +def _lvard(ins): + """ Defines a local variable. 1st param is offset of the local variable. + 2nd param is a list of bytes in hexadecimal. + """ + output = [] + + label = tmp_label() + offset = int(ins.quad[1]) + tmp = list(ins.quad) + tmp[1] = label + ins.quad = tmp + AT_END.extend(_vard(ins)) + + output.append('push ix') + output.append('pop hl') + output.append('ld bc, %i' % -offset) + output.append('add hl, bc') + output.append('ex de, hl') + output.append('ld hl, %s' % label) + output.append('ld bc, %i' % get_bytes_size(eval(tmp[2]))) + output.append('ldir') + + return output + + +def _larrd(ins): + """ Defines a local array. + - 1st param is offset of the local variable. + - 2nd param is a list of bytes in hexadecimal corresponding to the index table + - 3rd param is the size of elements in byte + - 4th param is a list (might be empty) of byte to initialize the array with + - 5th param is a list (might be empty or 2 elements) of [lbound, ubound] labels. + """ + output = [] + + label = tmp_label() + offset = int(ins.quad[1]) + elements_size = ins.quad[3] + AT_END.extend(_vard(Quad('vard', label, ins.quad[2]))) + + bounds = eval(ins.quad[5]) + if not isinstance(bounds, list) or len(bounds) not in (0, 2): + raise InvalidIC(ins.quad, 'Bounds list length must be 0 or 2, not %s' % ins.quad[5]) + + if bounds: + output.extend([ + 'ld hl, %s' % bounds[1], + 'push hl', + 'ld hl, %s' % bounds[0], + 'push hl', + ]) + + must_initialize = ins.quad[4] != '[]' + if must_initialize: + label2 = tmp_label() + AT_END.extend(_vard(Quad('vard', label2, ins.quad[4]))) + output.extend([ + 'ld hl, %s' % label2, + 'push hl' + ]) + + output.extend([ + 'ld hl, %i' % -offset, + 'ld de, %s' % label, + 'ld bc, %s' % elements_size, + ]) + + suffix = '_WITH_BOUNDS' if bounds else '' + if must_initialize: + output.append('call __ALLOC_INITIALIZED_LOCAL_ARRAY' + suffix) + else: + output.append('call __ALLOC_LOCAL_ARRAY' + suffix) + + REQUIRES.add('arrayalloc.asm') + return output + + +def _out(ins): + """ Translates OUT to asm. + """ + output = _8bit_oper(ins.quad[2]) + output.extend(_16bit_oper(ins.quad[1])) + output.append('ld b, h') + output.append('ld c, l') + output.append('out (c), a') + + return output + + +def _in(ins): + """ Translates IN to asm. + """ + output = _16bit_oper(ins.quad[1]) + output.append('ld b, h') + output.append('ld c, l') + output.append('in a, (c)') + output.append('push af') + + return output + + +def _load8(ins): + """ Loads an 8 bit value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + """ + output = _8bit_oper(ins.quad[2]) + output.append('push af') + return output + + +def _load16(ins): + """ Loads a 16 bit value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + """ + output = _16bit_oper(ins.quad[2]) + output.append('push hl') + return output + + +def _load32(ins): + """ Load a 32 bit value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + """ + output = _32bit_oper(ins.quad[2]) + output.append('push de') + output.append('push hl') + return output + + +def _loadf16(ins): + """ Load a 32 bit (16.16) fixed point value from a memory address + If 2nd arg. start with '*', it is always treated as + an indirect value. + """ + output = _f16_oper(ins.quad[2]) + output.append('push de') + output.append('push hl') + return output + + +def _loadf(ins): + """ Loads a floating point value from a memory address. + If 2nd arg. start with '*', it is always treated as + an indirect value. + """ + output = _float_oper(ins.quad[2]) + output.extend(_fpush()) + return output + + +def _loadstr(ins): + """ Loads a string value from a memory address. + """ + temporal, output = _str_oper(ins.quad[2], no_exaf=True) + + if not temporal: + output.append('call __LOADSTR') + REQUIRES.add('loadstr.asm') + + output.append('push hl') + return output + + +def _store8(ins): + """ Stores 2nd operand content into address of 1st operand. + store8 a, x => a = x + Use '*' for indirect store on 1st operand. + """ + output = _8bit_oper(ins.quad[2]) + + op = ins.quad[1] + + indirect = op[0] == '*' + if indirect: + op = op[1:] + + immediate = op[0] == '#' + if immediate: + op = op[1:] + + if is_int(op) or op[0] == '_': + if is_int(op): + op = str(int(op) & 0xFFFF) + + if immediate: + if indirect: + output.append('ld (%s), a' % op) + else: # ??? + output.append('ld (%s), a' % op) + elif indirect: + output.append('ld hl, (%s)' % op) + output.append('ld (hl), a') + else: + output.append('ld (%s), a' % op) + else: + if immediate: + if indirect: # A label not starting with _ + output.append('ld hl, (%s)' % op) + output.append('ld (hl), a') + else: + output.append('ld (%s), a' % op) + return output + else: + output.append('pop hl') + + if indirect: + output.append('ld e, (hl)') + output.append('inc hl') + output.append('ld d, (hl)') + output.append('ld (de), a') + else: + output.append('ld (hl), a') + + return output + + +def _store16(ins): + """ Stores 2nd operand content into address of 1st operand. + store16 a, x => *(&a) = x + Use '*' for indirect store on 1st operand. + """ + output = [] + output = _16bit_oper(ins.quad[2]) + + try: + value = ins.quad[1] + indirect = False + if value[0] == '*': + indirect = True + value = value[1:] + + value = int(value) & 0xFFFF + if indirect: + output.append('ex de, hl') + output.append('ld hl, (%s)' % str(value)) + output.append('ld (hl), e') + output.append('inc hl') + output.append('ld (hl), d') + else: + output.append('ld (%s), hl' % str(value)) + except ValueError: + if value[0] == '_': + if indirect: + output.append('ex de, hl') + output.append('ld hl, (%s)' % str(value)) + output.append('ld (hl), e') + output.append('inc hl') + output.append('ld (hl), d') + else: + output.append('ld (%s), hl' % str(value)) + elif value[0] == '#': + value = value[1:] + if indirect: + output.append('ex de, hl') + output.append('ld hl, (%s)' % str(value)) + output.append('ld (hl), e') + output.append('inc hl') + output.append('ld (hl), d') + else: + output.append('ld (%s), hl' % str(value)) + else: + output.append('ex de, hl') + if indirect: + output.append('pop hl') + output.append('ld a, (hl)') + output.append('inc hl') + output.append('ld h, (hl)') + output.append('ld l, a') + else: + output.append('pop hl') + + output.append('ld (hl), e') + output.append('inc hl') + output.append('ld (hl), d') + + return output + + +def _store32(ins): + """ Stores 2nd operand content into address of 1st operand. + store16 a, x => *(&a) = x + """ + op = ins.quad[1] + + indirect = op[0] == '*' + if indirect: + op = op[1:] + + immediate = op[0] == '#' # Might make no sense here? + if immediate: + op = op[1:] + + if is_int(op) or op[0] == '_' or immediate: + output = _32bit_oper(ins.quad[2], preserveHL=indirect) + + if is_int(op): + op = str(int(op) & 0xFFFF) + + if indirect: + output.append('ld hl, (%s)' % op) + output.append('call __STORE32') + REQUIRES.add('store32.asm') + + return output + + output.append('ld (%s), hl' % op) + output.append('ld (%s + 2), de' % op) + + return output + + output = _32bit_oper(ins.quad[2], preserveHL=True) + output.append('pop hl') + + if indirect: + output.append('call __ISTORE32') + REQUIRES.add('store32.asm') + + return output + + output.append('call __STORE32') + REQUIRES.add('store32.asm') + + return output + + +def _storef16(ins): + """ Stores 2º operand content into address of 1st operand. + store16 a, x => *(&a) = x + """ + value = ins.quad[2] + if is_float(value): + val = float(ins.quad[2]) # Immediate? + (de, hl) = f16(val) + q = list(ins.quad) + q[2] = (de << 16) | hl + ins.quad = tuple(q) + + return _store32(ins) + + +def _storef(ins): + """ Stores a floating point value into a memory address. + """ + output = _float_oper(ins.quad[2]) + + op = ins.quad[1] + + indirect = op[0] == '*' + if indirect: + op = op[1:] + + immediate = op[0] == '#' # Might make no sense here? + if immediate: + op = op[1:] + + if is_int(op) or op[0] == '_': + if is_int(op): + op = str(int(op) & 0xFFFF) + + if indirect: + output.append('ld hl, (%s)' % op) + else: + output.append('ld hl, %s' % op) + else: + output.append('pop hl') + if indirect: + output.append('call __ISTOREF') + REQUIRES.add('storef.asm') + + return output + + output.append('call __STOREF') + REQUIRES.add('storef.asm') + + return output + + +def _storestr(ins): + """ Stores a string value into a memory address. + It copies content of 2nd operand (string), into 1st, reallocating + dynamic memory for the 1st str. These instruction DOES ALLOW + inmediate strings for the 2nd parameter, starting with '#'. + + Must prepend '#' (immediate sigil) to 1st operand, as we need + the & address of the destination. + """ + op1 = ins.quad[1] + indirect = op1[0] == '*' + if indirect: + op1 = op1[1:] + + immediate = op1[0] == '#' + if immediate and not indirect: + raise InvalidIC('storestr does not allow immediate destination', ins.quad) + + if not indirect: + op1 = '#' + op1 + + tmp1, tmp2, output = _str_oper(op1, ins.quad[2], no_exaf=True) + + if not tmp2: + output.append('call __STORE_STR') + REQUIRES.add('storestr.asm') + else: + output.append('call __STORE_STR2') + REQUIRES.add('storestr2.asm') + + return output + + +def _cast(ins): + """ Convert data from typeA to typeB (only numeric data types) + """ + # Signed and unsigned types are the same in the Z80 + tA = ins.quad[2] # From TypeA + tB = ins.quad[3] # To TypeB + + YY_TYPES[tA] # Type sizes + xsB = sB = YY_TYPES[tB] # Type sizes + + output = [] + if tA in ('u8', 'i8'): + output.extend(_8bit_oper(ins.quad[4])) + elif tA in ('u16', 'i16'): + output.extend(_16bit_oper(ins.quad[4])) + elif tA in ('u32', 'i32'): + output.extend(_32bit_oper(ins.quad[4])) + elif tA == 'f16': + output.extend(_f16_oper(ins.quad[4])) + elif tA == 'f': + output.extend(_float_oper(ins.quad[4])) + else: + raise errors.GenericError( + 'Internal error: invalid typecast from %s to %s' % (tA, tB)) + + if tB in ('u8', 'i8'): # It was a byte + output.extend(to_byte(tA)) + elif tB in ('u16', 'i16'): + output.extend(to_word(tA)) + elif tB in ('u32', 'i32'): + output.extend(to_long(tA)) + elif tB == 'f16': + output.extend(to_fixed(tA)) + elif tB == 'f': + output.extend(to_float(tA)) + + xsB += sB % 2 # make it even (round up) + + if xsB > 4: + output.extend(_fpush()) + else: + if xsB > 2: + output.append('push de') # Fixed or 32 bit Integer + + if sB > 1: + output.append('push hl') # 16 bit Integer + else: + output.append('push af') # 8 bit Integer + + return output + + +# ------------------- FLOW CONTROL instructions ------------------- + +def _jump(ins): + """ Jump to a label + """ + return ['jp %s' % str(ins.quad[1])] + + +def _jzero8(ins): + """ Jumps if top of the stack (8bit) is 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) == 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _8bit_oper(value) + output.append('or a') + output.append('jp z, %s' % str(ins.quad[2])) + return output + + +def _jzero16(ins): + """ Jumps if top of the stack (16bit) is 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) == 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _16bit_oper(value) + output.append('ld a, h') + output.append('or l') + output.append('jp z, %s' % str(ins.quad[2])) + return output + + +def _jzero32(ins): + """ Jumps if top of the stack (32bit) is 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) == 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _32bit_oper(value) + output.append('ld a, h') + output.append('or l') + output.append('or e') + output.append('or d') + output.append('jp z, %s' % str(ins.quad[2])) + return output + + +def _jzerof16(ins): + """ Jumps if top of the stack (32bit) is 0 to arg(1) + (For Fixed point 16.16 bit values) + """ + value = ins.quad[1] + if is_float(value): + if float(value) == 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _f16_oper(value) + output.append('ld a, h') + output.append('or l') + output.append('or e') + output.append('or d') + output.append('jp z, %s' % str(ins.quad[2])) + return output + + +def _jzerof(ins): + """ Jumps if top of the stack (40bit, float) is 0 to arg(1) + """ + value = ins.quad[1] + if is_float(value): + if float(value) == 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _float_oper(value) + output.append('ld a, c') + output.append('or l') + output.append('or h') + output.append('or e') + output.append('or d') + output.append('jp z, %s' % str(ins.quad[2])) + return output + + +def _jzerostr(ins): + """ Jumps if top of the stack contains a NULL pointer + or its len is Zero + """ + output = [] + disposable = False # True if string must be freed from memory + + if ins.quad[1][0] == '_': # Variable? + output.append('ld hl, (%s)' % ins.quad[1][0]) + else: + output.append('pop hl') + output.append('push hl') # Saves it for later + disposable = True + + output.append('call __STRLEN') + + if disposable: + output.append('ex (sp), hl') + output.append('call __MEM_FREE') + output.append('pop hl') + REQUIRES.add('alloc.asm') + + output.append('ld a, h') + output.append('or l') + output.append('jp z, %s' % str(ins.quad[2])) + REQUIRES.add('strlen.asm') + return output + + +def _jnzero8(ins): + """ Jumps if top of the stack (8bit) is != 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) != 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _8bit_oper(value) + output.append('or a') + output.append('jp nz, %s' % str(ins.quad[2])) + return output + + +def _jnzero16(ins): + """ Jumps if top of the stack (16bit) is != 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) != 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _16bit_oper(value) + output.append('ld a, h') + output.append('or l') + output.append('jp nz, %s' % str(ins.quad[2])) + return output + + +def _jnzero32(ins): + """ Jumps if top of the stack (32bit) is !=0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) != 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _32bit_oper(value) + output.append('ld a, h') + output.append('or l') + output.append('or e') + output.append('or d') + output.append('jp nz, %s' % str(ins.quad[2])) + return output + + +def _jnzerof16(ins): + """ Jumps if top of the stack (32bit) is !=0 to arg(1) + Fixed Point (16.16 bit) values. + """ + value = ins.quad[1] + if is_float(value): + if float(value) != 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _f16_oper(value) + output.append('ld a, h') + output.append('or l') + output.append('or e') + output.append('or d') + output.append('jp nz, %s' % str(ins.quad[2])) + return output + + +def _jnzerof(ins): + """ Jumps if top of the stack (40bit, float) is != 0 to arg(1) + """ + value = ins.quad[1] + if is_float(value): + if float(value) != 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _float_oper(value) + output.append('ld a, c') + output.append('or l') + output.append('or h') + output.append('or e') + output.append('or d') + output.append('jp nz, %s' % str(ins.quad[2])) + return output + + +def _jnzerostr(ins): + """ Jumps if top of the stack contains a string with + at less 1 char + """ + output = [] + disposable = False # True if string must be freed from memory + + if ins.quad[1][0] == '_': # Variable? + output.append('ld hl, (%s)' % ins.quad[1][0]) + else: + output.append('pop hl') + output.append('push hl') # Saves it for later + disposable = True + + output.append('call __STRLEN') + + if disposable: + output.append('ex (sp), hl') + output.append('call __MEM_FREE') + output.append('pop hl') + REQUIRES.add('alloc.asm') + + output.append('ld a, h') + output.append('or l') + output.append('jp nz, %s' % str(ins.quad[2])) + + REQUIRES.add('strlen.asm') + + return output + + +def _jgezerou8(ins): + """ Jumps if top of the stack (8bit) is >= 0 to arg(1) + Always TRUE for unsigned + """ + output = [] + value = ins.quad[1] + if not is_int(value): + output = _8bit_oper(value) + + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _jgezeroi8(ins): + """ Jumps if top of the stack (8bit) is >= 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) >= 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _8bit_oper(value) + output.append('add a, a') # Puts sign into carry + output.append('jp nc, %s' % str(ins.quad[2])) + return output + + +def _jgezerou16(ins): + """ Jumps if top of the stack (16bit) is >= 0 to arg(1) + Always TRUE for unsigned + """ + output = [] + value = ins.quad[1] + if not is_int(value): + output = _16bit_oper(value) + + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _jgezeroi16(ins): + """ Jumps if top of the stack (16bit) is >= 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) >= 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _16bit_oper(value) + output.append('add hl, hl') # Puts sign into carry + output.append('jp nc, %s' % str(ins.quad[2])) + return output + + +def _jgezerou32(ins): + """ Jumps if top of the stack (23bit) is >= 0 to arg(1) + Always TRUE for unsigned + """ + output = [] + value = ins.quad[1] + if not is_int(value): + output = _32bit_oper(value) + + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _jgezeroi32(ins): + """ Jumps if top of the stack (32bit) is >= 0 to arg(1) + """ + value = ins.quad[1] + if is_int(value): + if int(value) >= 0: + return ['jp %s' % str(ins.quad[2])] # Always true + else: + return [] + + output = _32bit_oper(value) + output.append('ld a, d') + output.append('add a, a') # Puts sign into carry + output.append('jp nc, %s' % str(ins.quad[2])) + return output + + +def _jgezerof16(ins): + """ Jumps if top of the stack (32bit, fixed point) is >= 0 to arg(1) + """ + value = ins.quad[1] + if is_float(value): + if float(value) >= 0: + return ['jp %s' % str(ins.quad[2])] # Always true + + output = _f16_oper(value) + output.append('ld a, d') + output.append('add a, a') # Puts sign into carry + output.append('jp nc, %s' % str(ins.quad[2])) + return output + + +def _jgezerof(ins): + """ Jumps if top of the stack (40bit, float) is >= 0 to arg(1) + """ + value = ins.quad[1] + if is_float(value): + if float(value) >= 0: + return ['jp %s' % str(ins.quad[2])] # Always true + + output = _float_oper(value) + output.append('ld a, e') # Take sign from mantissa (bit 7) + output.append('add a, a') # Puts sign into carry + output.append('jp nc, %s' % str(ins.quad[2])) + return output + + +def _ret(ins): + """ Returns from a procedure / function + """ + return ['jp %s' % str(ins.quad[1])] + + +def _ret8(ins): + """ Returns from a procedure / function an 8bits value + """ + output = _8bit_oper(ins.quad[1]) + output.append('#pragma opt require a') + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _ret16(ins): + """ Returns from a procedure / function a 16bits value + """ + output = _16bit_oper(ins.quad[1]) + output.append('#pragma opt require hl') + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _ret32(ins): + """ Returns from a procedure / function a 32bits value (even Fixed point) + """ + output = _32bit_oper(ins.quad[1]) + output.append('#pragma opt require hl,de') + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _retf16(ins): + """ Returns from a procedure / function a Fixed Point (32bits) value + """ + output = _f16_oper(ins.quad[1]) + output.append('#pragma opt require hl,de') + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _retf(ins): + """ Returns from a procedure / function a Floating Point (40bits) value + """ + output = _float_oper(ins.quad[1]) + output.append('#pragma opt require a,bc,de') + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _retstr(ins): + """ Returns from a procedure / function a string pointer (16bits) value + """ + tmp, output = _str_oper(ins.quad[1], no_exaf=True) + + if not tmp: + output.append('call __LOADSTR') + REQUIRES.add('loadstr.asm') + + output.append('#pragma opt require hl') + output.append('jp %s' % str(ins.quad[2])) + return output + + +def _call(ins): + """ Calls a function XXXX (or address XXXX) + 2nd parameter contains size of the returning result if any, and will be + pushed onto the stack. + """ + output = [] + output.append('call %s' % str(ins.quad[1])) + + try: + val = int(ins.quad[2]) + if val == 1: + output.append('push af') # Byte + else: + if val > 4: + output.extend(_fpush()) + else: + if val > 2: + output.append('push de') + if val > 1: + output.append('push hl') + + except ValueError: + pass + + return output + + +def _leave(ins): + """ Return from a function popping N bytes from the stack + Use '__fastcall__' as 1st parameter, to just return + """ + global FLAG_use_function_exit + + output = [] + + if ins.quad[1] == '__fastcall__': + output.append('ret') + return output + + nbytes = int(ins.quad[1]) # Number of bytes to pop (params size) + + if nbytes == 0: + output.append('ld sp, ix') + output.append('pop ix') + output.append('ret') + + return output + + if nbytes == 1: + output.append('ld sp, ix') + output.append('pop ix') + output.append('inc sp') # "Pops" 1 byte + output.append('ret') + + return output + + if nbytes <= 11: # Number of bytes it worth the hassle to "pop" off the stack + output.append('ld sp, ix') + output.append('pop ix') + output.append('exx') + output.append('pop hl') + for i in range((nbytes >> 1) - 1): + output.append('pop bc') # Removes (n * 2 - 2) bytes form the stack + + if nbytes & 1: # Odd? + output.append('inc sp') # "Pops" 1 byte (This should never happens, since params are always even-sized) + + output.append('ex (sp), hl') # Place back return address + output.append('exx') + output.append('ret') + + return output + + if not FLAG_use_function_exit: + FLAG_use_function_exit = True # Use standard exit + output.append('exx') + output.append('ld hl, %i' % nbytes) + output.append('__EXIT_FUNCTION:') + output.append('ld sp, ix') + output.append('pop ix') + output.append('pop de') + output.append('add hl, sp') + output.append('ld sp, hl') + output.append('push de') + output.append('exx') + output.append('ret') + else: + output.append('exx') + output.append('ld hl, %i' % nbytes) + output.append('jp __EXIT_FUNCTION') + + return output + + +def _enter(ins): + """ Enter function sequence for doing a function start + ins.quad[1] contains size (in bytes) of local variables + Use '__fastcall__' as 1st parameter to prepare a fastcall + function (no local variables). + """ + output = [] + + if ins.quad[1] == '__fastcall__': + return output + + output.append('push ix') + output.append('ld ix, 0') + output.append('add ix, sp') + + size_bytes = int(ins.quad[1]) + + if size_bytes != 0: + if size_bytes < 7: + output.append('ld hl, 0') + output.extend(['push hl'] * (size_bytes >> 1)) + + if size_bytes % 2: # odd? + output.append('push hl') + output.append('inc sp') + else: + output.append('ld hl, -%i' % size_bytes) # "Pushes nn bytes" + output.append('add hl, sp') + output.append('ld sp, hl') + output.append('ld (hl), 0') + output.append('ld bc, %i' % (size_bytes - 1)) + output.append('ld d, h') + output.append('ld e, l') + output.append('inc de') + output.append('ldir') # Clear with ZEROs + + return output + + +def _param8(ins): + """ Pushes 8bit param into the stack + """ + output = _8bit_oper(ins.quad[1]) + output.append('push af') + return output + + +def _param16(ins): + """ Pushes 16bit param into the stack + """ + output = _16bit_oper(ins.quad[1]) + output.append('push hl') + return output + + +def _param32(ins): + """ Pushes 32bit param into the stack + """ + output = _32bit_oper(ins.quad[1]) + output.append('push de') + output.append('push hl') + return output + + +def _paramf16(ins): + """ Pushes 32bit fixed point param into the stack + """ + output = _f16_oper(ins.quad[1]) + output.append('push de') + output.append('push hl') + return output + + +def _paramf(ins): + """ Pushes 40bit (float) param into the stack + """ + output = _float_oper(ins.quad[1]) + output.extend(_fpush()) + return output + + +def _paramstr(ins): + """ Pushes an 16 bit unsigned value, which points + to a string. For indirect values, it will push + the pointer to the pointer :-) + """ + (tmp, output) = _str_oper(ins.quad[1]) + output.pop() # Remove a register flag (useless here) + tmp = ins.quad[1][0] in ('#', '_') # Determine if the string must be duplicated + + if tmp: + output.append('call __LOADSTR') # Must be duplicated + REQUIRES.add('loadstr.asm') + + output.append('push hl') + return output + + +def _fparam8(ins): + """ Passes a byte as a __FASTCALL__ parameter. + This is done by popping out of the stack for a + value, or by loading it from memory (indirect) + or directly (immediate) + """ + return _8bit_oper(ins.quad[1]) + + +def _fparam16(ins): + """ Passes a word as a __FASTCALL__ parameter. + This is done by popping out of the stack for a + value, or by loading it from memory (indirect) + or directly (immediate) + """ + return _16bit_oper(ins.quad[1]) + + +def _fparam32(ins): + """ Passes a dword as a __FASTCALL__ parameter. + This is done by popping out of the stack for a + value, or by loading it from memory (indirect) + or directly (immediate) + """ + return _32bit_oper(ins.quad[1]) + + +def _fparamf16(ins): + """ Passes a 16.16 fixed point as a __FASTCALL__ parameter. + This is done by popping out of the stack for a + value, or by loading it from memory (indirect) + or directly (immediate) + """ + return _f16_oper(ins.quad[1]) + + +def _fparamf(ins): + """ Passes a floating point as a __FASTCALL__ parameter. + This is done by popping out of the stack for a + value, or by loading it from memory (indirect) + or directly (immediate) + """ + return _float_oper(ins.quad[1]) + + +def _fparamstr(ins): + """ Passes a string ptr as a __FASTCALL__ parameter. + This is done by popping out of the stack for a + value, or by loading it from memory (indirect) + or directly (immediate) --prefixed with '#'-- + """ + (tmp1, output) = _str_oper(ins.quad[1]) + + return output + + +def _memcopy(ins): + """ Copies a block of memory from param 2 addr + to param 1 addr. + """ + output = _16bit_oper(ins.quad[3]) + output.append('ld b, h') + output.append('ld c, l') + output.extend(_16bit_oper(ins.quad[1], ins.quad[2], reversed=True)) + output.append('ldir') # *** + + return output + + +def _inline(ins): + """ Inline code + """ + tmp = [x.strip(' \t\r\n') for x in ins.quad[1].split('\n')] # Split lines + + i = 0 + while i < len(tmp): + if not tmp[i] or tmp[i][0] == ';': # a comment or empty string? + tmp.pop(i) + continue + + if tmp[i][0] == '#': # A preprocessor directive + i += 1 + continue + + match = RE_LABEL.match(tmp[i]) + if not match: + tmp[i] = '\t' + tmp[i] + i += 1 + continue + + if len(tmp[i][-1]) == ':': + i += 1 + continue # This is already a single label + + tmp[i] = tmp[i][match.end() + 1:].strip(' \n') + tmp.insert(i, match.group()) + i += 1 + + output = [] + if not tmp: + return output + + ASMLABEL = new_ASMID() + ASMS[ASMLABEL] = tmp + output.append('#line %s' % ins.quad[2]) + output.append(ASMLABEL) + output.append('#line %i' % (int(ins.quad[2]) + len(tmp))) + + return output + + +# -------- 3 address code implementation ---------- + +class Quad(object): + """ Implements a Quad code instruction. + """ + + def __init__(self, *args): + """ Creates a quad-uple checking it has the current params. + Operators should be passed as Quad('+', tSymbol, val1, val2) + """ + if not args: + raise InvalidIC('') + + if args[0] not in QUADS.keys(): + errors.throw_invalid_quad_code(args[0]) + + if len(args) - 1 != QUADS[args[0]][0]: + errors.throw_invalid_quad_params(args[0], len(args) - 1) + + args = tuple([str(x) for x in args]) # Convert it to strings + + self.quad = args + self.op = args[0] + + def __str__(self): + """ String representation + """ + return str(self.quad) + + +# Table describing operations +# 'OPERATOR' -> [Number of arguments] +QUADS = { + 'addu8': [3, _add8], + 'addi8': [3, _add8], + 'addi16': [3, _add16], + 'addu16': [3, _add16], + 'addi32': [3, _add32], + 'addu32': [3, _add32], + 'addf16': [3, _addf16], + 'addf': [3, _addf], + + 'addstr': [3, _addstr], + + 'data': [2, _data], + + 'subi8': [3, _sub8], + 'subu8': [3, _sub8], + 'subi16': [3, _sub16], + 'subu16': [3, _sub16], + 'subi32': [3, _sub32], + 'subu32': [3, _sub32], + 'subf16': [3, _subf16], + 'subf': [3, _subf], + + 'muli8': [3, _mul8], + 'mulu8': [3, _mul8], + 'muli16': [3, _mul16], + 'mulu16': [3, _mul16], + 'muli32': [3, _mul32], + 'mulu32': [3, _mul32], + 'mulf16': [3, _mulf16], + 'mulf': [3, _mulf], + + 'divu8': [3, _divu8], + 'divi8': [3, _divi8], + 'divu16': [3, _divu16], + 'divi16': [3, _divi16], + 'divu32': [3, _divu32], + 'divi32': [3, _divi32], + 'divf16': [3, _divf16], + 'divf': [3, _divf], + + 'powf': [3, _powf], + + 'modu8': [3, _modu8], + 'modi8': [3, _modi8], + 'modu16': [3, _modu16], + 'modi16': [3, _modi16], + 'modu32': [3, _modu32], + 'modi32': [3, _modi32], + 'modf16': [3, _modf16], + 'modf': [3, _modf], + + 'shru8': [3, _shru8], + 'shri8': [3, _shri8], + 'shlu8': [3, _shl8], + 'shli8': [3, _shl8], + + 'shru16': [3, _shru16], + 'shri16': [3, _shri16], + 'shlu16': [3, _shl16], + 'shli16': [3, _shl16], + + 'shru32': [3, _shru32], + 'shri32': [3, _shri32], + 'shlu32': [3, _shl32], + 'shli32': [3, _shl32], + + 'ltu8': [3, _ltu8], + 'lti8': [3, _lti8], + 'ltu16': [3, _ltu16], + 'lti16': [3, _lti16], + 'ltu32': [3, _ltu32], + 'lti32': [3, _lti32], + 'ltf16': [3, _ltf16], + 'ltf': [3, _ltf], + 'ltstr': [3, _ltstr], + + 'gtu8': [3, _gtu8], + 'gti8': [3, _gti8], + 'gtu16': [3, _gtu16], + 'gti16': [3, _gti16], + 'gtu32': [3, _gtu32], + 'gti32': [3, _gti32], + 'gtf16': [3, _gtf16], + 'gtf': [3, _gtf], + 'gtstr': [3, _gtstr], + + 'leu8': [3, _leu8], + 'lei8': [3, _lei8], + 'leu16': [3, _leu16], + 'lei16': [3, _lei16], + 'leu32': [3, _leu32], + 'lei32': [3, _lei32], + 'lef16': [3, _lef16], + 'lef': [3, _lef], + 'lestr': [3, _lestr], + + 'geu8': [3, _geu8], + 'gei8': [3, _gei8], + 'geu16': [3, _geu16], + 'gei16': [3, _gei16], + 'geu32': [3, _geu32], + 'gei32': [3, _gei32], + 'gef16': [3, _gef16], + 'gef': [3, _gef], + 'gestr': [3, _gestr], + + 'equ8': [3, _eq8], + 'eqi8': [3, _eq8], + 'equ16': [3, _eq16], + 'eqi16': [3, _eq16], + 'equ32': [3, _eq32], + 'eqi32': [3, _eq32], + 'eqf16': [3, _eqf16], + 'eqf': [3, _eqf], + 'eqstr': [3, _eqstr], + + 'neu8': [3, _ne8], + 'nei8': [3, _ne8], + 'neu16': [3, _ne16], + 'nei16': [3, _ne16], + 'neu32': [3, _ne32], + 'nei32': [3, _ne32], + 'nef16': [3, _nef16], + 'nef': [3, _nef], + 'nestr': [3, _nestr], + + 'absi8': [2, _abs8], # x = -x if x < 0 + 'absi16': [2, _abs16], # x = -x if x < 0 + 'absi32': [2, _abs32], # x = -x if x < 0 + 'absf16': [2, _absf16], # x = -x if x < 0 + 'absf': [2, _absf], # x = -x if x < 0 + + 'negu8': [2, _neg8], # x = -y + 'negi8': [2, _neg8], # x = -y + 'negu16': [2, _neg16], # x = -y + 'negi16': [2, _neg16], # x = -y + 'negu32': [2, _neg32], # x = -y + 'negi32': [2, _neg32], # x = -y + 'negf16': [2, _negf16], # x = -y + 'negf': [2, _negf], # x = -y + + 'andu8': [3, _and8], # x = A and B + 'andi8': [3, _and8], # x = A and B + 'andu16': [3, _and16], # x = A and B + 'andi16': [3, _and16], # x = A and B + 'andu32': [3, _and32], # x = A and B + 'andi32': [3, _and32], # x = A and B + 'andf16': [3, _andf16], # x = A and B + 'andf': [3, _andf], # x = A and B + + 'oru8': [3, _or8], # x = A or B + 'ori8': [3, _or8], # x = A or B + 'oru16': [3, _or16], # x = A or B + 'ori16': [3, _or16], # x = A or B + 'oru32': [3, _or32], # x = A or B + 'ori32': [3, _or32], # x = A or B + 'orf16': [3, _orf16], # x = A or B + 'orf': [3, _orf], # x = A or B + + 'xoru8': [3, _xor8], # x = A xor B + 'xori8': [3, _xor8], # x = A xor B + 'xoru16': [3, _xor16], # x = A xor B + 'xori16': [3, _xor16], # x = A xor B + 'xoru32': [3, _xor32], # x = A xor B + 'xori32': [3, _xor32], # x = A xor B + 'xorf16': [3, _xorf16], # x = A xor B + 'xorf': [3, _xorf], # x = A xor B + + 'notu8': [2, _not8], # x = not B + 'noti8': [2, _not8], # x = not B + 'notu16': [2, _not16], # x = not B + 'noti16': [2, _not16], # x = not B + 'notu32': [2, _not32], # x = not B + 'noti32': [2, _not32], # x = not B + 'notf16': [2, _notf16], # x = not B + 'notf': [2, _notf], # x = not B + + 'jump': [1, _jump], # jmp LABEL + + 'lenstr': [2, _lenstr], # Gets strlen + + 'jzeroi8': [2, _jzero8], # if X == 0 jmp LABEL + 'jzerou8': [2, _jzero8], # if X == 0 jmp LABEL + 'jzeroi16': [2, _jzero16], # if X == 0 jmp LABEL + 'jzerou16': [2, _jzero16], # if X == 0 jmp LABEL + 'jzeroi32': [2, _jzero32], # if X == 0 jmp LABEL (32bit, fixed) + 'jzerou32': [2, _jzero32], # if X == 0 jmp LABEL (32bit, fixed) + 'jzerof16': [2, _jzerof16], # if X == 0 jmp LABEL (32bit, fixed) + 'jzerof': [2, _jzerof], # if X == 0 jmp LABEL (float) + 'jzerostr': [2, _jzerostr], # if str is NULL or len(str) == 0, jmp LABEL + + 'jnzeroi8': [2, _jnzero8], # if X != 0 jmp LABEL + 'jnzerou8': [2, _jnzero8], # if X != 0 jmp LABEL + 'jnzeroi16': [2, _jnzero16], # if X != 0 jmp LABEL + 'jnzerou16': [2, _jnzero16], # if X != 0 jmp LABEL + 'jnzeroi32': [2, _jnzero32], # if X != 0 jmp LABEL (32bit, fixed) + 'jnzerou32': [2, _jnzero32], # if X != 0 jmp LABEL (32bit, fixed) + 'jnzerof16': [2, _jnzerof16], # if X != 0 jmp LABEL (32bit, fixed) + 'jnzerof': [2, _jnzerof], # if X != 0 jmp LABEL (float) + 'jnzerostr': [2, _jnzerostr], # if str is not NULL and len(str) > 0, jmp LABEL + + 'jgezeroi8': [2, _jgezeroi8], # if X >= 0 jmp LABEL + 'jgezerou8': [2, _jgezerou8], # if X >= 0 jmp LABEL (ALWAYS TRUE) + 'jgezeroi16': [2, _jgezeroi16], # if X >= 0 jmp LABEL + 'jgezerou16': [2, _jgezerou16], # if X >= 0 jmp LABEL (ALWAYS TRUE) + 'jgezeroi32': [2, _jgezeroi32], # if X >= 0 jmp LABEL (32bit, fixed) + 'jgezerou32': [2, _jgezerou32], # if X >= 0 jmp LABEL (32bit, fixed) (always true) + 'jgezerof16': [2, _jgezerof16], # if X >= 0 jmp LABEL (32bit, fixed) + 'jgezerof': [2, _jgezerof], # if X >= 0 jmp LABEL (float) + + 'paramu8': [1, _param8], # Push 8 bit param onto the stack + 'parami8': [1, _param8], # Push 8 bit param onto the stack + 'paramu16': [1, _param16], # Push 16 bit param onto the stack + 'parami16': [1, _param16], # Push 16 bit param onto the stack + 'paramu32': [1, _param32], # Push 32 bit param onto the stack + 'parami32': [1, _param32], # Push 32 bit param onto the stack + 'paramf16': [1, _paramf16], # Push 32 bit param onto the stack + 'paramf': [1, _paramf], # Push float param - 6 BYTES (always even) onto the stack + 'paramstr': [1, _paramstr], # Push float param - 6 BYTES (always even) onto the stack + + 'fparamu8': [1, _fparam8], # __FASTCALL__ parameter + 'fparami8': [1, _fparam8], # __FASTCALL__ parameter + 'fparamu16': [1, _fparam16], # __FASTCALL__ parameter + 'fparami16': [1, _fparam16], # __FASTCALL__ parameter + 'fparamu32': [1, _fparam32], # __FASTCALL__ parameter + 'fparami32': [1, _fparam32], # __FASTCALL__ parameter + 'fparamf16': [1, _fparamf16], # __FASTCALL__ parameter + 'fparamf': [1, _fparamf], # __FASTCALL__ parameter + 'fparamstr': [1, _fparamstr], # __FASTCALL__ parameter + + 'call': [2, _call], # Call Address, NNNN --- NNNN = Size (in bytes) of the returned value (0 for procedure) + + 'ret': [1, _ret], # Returns from a function call (enters the 'leave' sequence'), returning no value + 'reti8': [2, _ret8], # Returns from a function call (enters the 'leave' sequence'), returning 8 bit value + 'retu8': [2, _ret8], # Returns from a function call (enters the 'leave' sequence'), returning 8 bit value + 'reti16': [2, _ret16], # Returns from a function call (enters the 'leave' sequence'), returning 16 bit value + 'retu16': [2, _ret16], # Returns from a function call (enters the 'leave' sequence'), returning 16 bit value + 'reti32': [2, _ret32], # Returns from a function call (enters the 'leave' sequence'), returning 32 bit value + 'retu32': [2, _ret32], # Returns from a function call (enters the 'leave' sequence'), returning 32 bit value + 'retf16': [2, _retf16], # Returns from a function call (enters the 'leave' sequence'), returning fixed point + 'retf': [2, _retf], # Returns from a function call (enters the 'leave' sequence'), returning fixed point + 'retstr': [2, _retstr], # Returns from a function call (enters the 'leave' sequence'), returning fixed point + + 'leave': [1, _leave], # LEAVE label, NN -> NN = Size of parameters in bytes (End of function