# -*- coding: utf-8 -*-

# This code is part of Amoco
# Copyright (C) 2012 Axel Tillequin (bdcht3@gmail.com)
# published under GPLv2 license

# spec_xxx files are providers for instruction objects.
# These objects are wrapped and created by disasm.py.

from amoco.logger import Log

logger = Log(__name__)
logger.debug("loading module")
from amoco.arch.core import *
from amoco.arch.z80 import env

# modifications of spec_mostek.ISPECS according to GB specs...
# (all DD/FD prefixed spec are removed IX/IY

# remove unused registers:
del env.ix, env.iy
del env.ir
del env.i, env.r
del env.ixh, env.ixl
del env.iyh, env.iyl

# remove unused flags & conditions:
del env.pf
del env.xf
del env.yf
del env.sf

del env.CONDITION[0b100]
del env.CONDITION[0b101]
del env.CONDITION[0b110]
del env.CONDITION[0b111]

# update flags:
env.cf.pos = 4
env.hf.pos = 5
env.nf.pos = 6
env.zf.pos = 7

# Prefixes are removed (obj.misc['pfx'] is always None)
# simplified getreg8/getreg16


def getreg8(obj, x):
    r = env.reg8[x]
    return r


def getreg16(obj, x):
    if x == 0b11 and obj.mnemonic not in ("PUSH", "POP"):
        r = env.sp
    else:
        r = env.reg16[x]
    return r


ISPECS = []

# ----------------
# 8-bit load group
# ----------------

# LD r,r'
@ispec("8<[ 01 rd(3) rs(3) ]", mnemonic="LD")
def mostek_ld(obj, rd, rs):
    dst, src = getreg8(obj, rd), getreg8(obj, rs)
    if dst._is_mem or src._is_mem:
        if rd == rs or (obj.misc["pfx"] is not None):
            raise InstructionError(obj)
    obj.operands = [dst, src]
    obj.type = type_data_processing


# LD r,n
@ispec("16<[ n(8) 00 r(3) 110 ]", mnemonic="LD")
def mostek_ld(obj, r, n):
    dst = getreg8(obj, r)
    if r == 0b110 and obj.misc["pfx"] is not None:
        raise InstructionError(obj)
    obj.operands = [dst, env.cst(n, 8)]
    obj.type = type_data_processing


# LD (BC/DE), A
@ispec("8<[ 000 b rev 010 ]", mnemonic="LD")
def mostek_ld(obj, b, rev):
    base = env.reg16[b]
    obj.operands = [env.mem(base, 8), env.a]
    if rev:
        obj.operands.reverse()
    obj.type = type_data_processing


# LDD/LDI a,(hl)
@ispec("8<[ {3a} ]", mnemonic="LDD")
@ispec("8<[ {2a} ]", mnemonic="LDI")
def mostek_ld(obj):
    obj.operands = [env.a, env.reg8[0b110]]
    obj.type = type_data_processing


@ispec("8<[ {32} ]", mnemonic="LDD")
@ispec("8<[ {22} ]", mnemonic="LDI")
def mostek_ld(obj):
    obj.operands = [env.reg8[0b110], env.a]
    obj.type = type_data_processing


@ispec("8<[ {f2} ]", mnemonic="LD")
def mostek_ld(obj):
    base = env.composer([env.c, env.cst(0xFF, 8)])
    obj.operands = [env.a, env.mem(base, 8)]
    obj.type = type_data_processing


@ispec("8<[ {e2} ]", mnemonic="LD")
def mostek_ld(obj):
    base = env.composer([env.c, env.cst(0xFF, 8)])
    obj.operands = [env.mem(base, 8), env.a]
    obj.type = type_data_processing


@ispec("16<[ n(8) {f0} ]", mnemonic="LD")
def mostek_ld(obj, n):
    base = env.cst(0xFF00, 16) + n
    obj.operands = [env.a, env.mem(base, 8)]
    obj.type = type_data_processing


@ispec("16<[ n(8) {e0} ]", mnemonic="LD")
def mostek_ld(obj, n):
    base = env.cst(0xFF00, 16) + n
    obj.operands = [env.mem(base, 8), env.a]
    obj.type = type_data_processing


@ispec("24<[ n(16) {fa} ]", mnemonic="LD")
def mostek_ld(obj, n):
    base = env.cst(n, 16)
    obj.operands = [env.a, env.mem(base, 8)]
    obj.type = type_data_processing


@ispec("24<[ n(16) {ea} ]", mnemonic="LD")
def mostek_ld(obj, n):
    base = env.cst(n, 16)
    obj.operands = [env.mem(base, 8), env.a]
    obj.type = type_data_processing


# -----------------
# 16-bit load group
# -----------------

# LD dd,nn
@ispec("24<[ nn(16) 00 dd(2) 0001 ]", mnemonic="LD")
def mostek_ld(obj, dd, nn):
    dst = getreg16(obj, dd)
    obj.operands = [dst, env.cst(nn, 16)]
    obj.type = type_data_processing


# LD hl,(nn) / LD (nn), hl
@ispec("24<[ nn(16) 00 10 rev 010 ]", mnemonic="LD")
def mostek_ld(obj, rev, nn):
    dst = getreg16(obj, 0b10)
    obj.operands = [dst, env.mem(env.cst(nn, 16), 16)]
    if not rev:
        obj.operands.reverse()
    obj.type = type_data_processing


# LD SP,HL
@ispec("8<[ 1111 1001 ]", mnemonic="LD")
def mostek_ld(obj):
    dst = getreg16(obj, 0b10)
    obj.operands = [env.sp, dst]
    obj.type = type_data_processing


# PUSH qq
@ispec("8<[ 11 qq(2) 0101 ]", mnemonic="PUSH")
@ispec("8<[ 11 qq(2) 0001 ]", mnemonic="POP")
def mostek_ld(obj, qq):
    src = getreg16(obj, qq)
    if src == env.sp:
        src = env.af
    obj.operands = [src]
    obj.type = type_data_processing


# LDHL SP,n
@ispec("16<[ n(8) {f8} ]", mnemonic="LDHL")
def mostek_ld(obj, n):
    disp = env.cst(n, 8).signextend(16)
    obj.operands = [env.sp, disp]
    obj.type = type_data_processing


# LD (nn), SP
@ispec("24<[ nn(16) {08} ]", mnemonic="LD")
def mostek_ld(obj, nn):
    obj.operands = [env.mem(env.cst(nn, 16), 16), env.sp]
    obj.type = type_data_processing


# ----------------------
# 8-bit Arithmetic Group
# ----------------------

# ADD a,r
@ispec("8<[ 1000 0 r(3) ]", mnemonic="ADD")
@ispec("8<[ 1000 1 r(3) ]", mnemonic="ADC")
@ispec("8<[ 1001 0 r(3) ]", mnemonic="SUB")
@ispec("8<[ 1001 1 r(3) ]", mnemonic="SBC")
@ispec("8<[ 1010 0 r(3) ]", mnemonic="AND")
@ispec("8<[ 1011 0 r(3) ]", mnemonic="OR")
@ispec("8<[ 1010 1 r(3) ]", mnemonic="XOR")
@ispec("8<[ 1011 1 r(3) ]", mnemonic="CP")
@ispec("8<[ 00 r(3) 100 ]", mnemonic="INC")
@ispec("8<[ 00 r(3) 101 ]", mnemonic="DEC")
def mostek_arithmetic(obj, r):
    if r == 0b110 and obj.misc["pfx"] is not None:
        raise InstructionError(obj)
    src = getreg8(obj, r)
    obj.operands = [env.a, src]
    if obj.mnemonic in ("INC", "DEC"):
        obj.operands.pop(0)
    obj.type = type_data_processing


@ispec("16<[ n(8) 1100 0110 ]", mnemonic="ADD")
@ispec("16<[ n(8) 1100 1110 ]", mnemonic="ADC")
@ispec("16<[ n(8) 1101 0110 ]", mnemonic="SUB")
@ispec("16<[ n(8) 1101 1110 ]", mnemonic="SBC")
@ispec("16<[ n(8) 1110 0110 ]", mnemonic="AND")
@ispec("16<[ n(8) 1111 0110 ]", mnemonic="OR")
@ispec("16<[ n(8) 1110 1110 ]", mnemonic="XOR")
@ispec("16<[ n(8) 1111 1110 ]", mnemonic="CP")
def mostek_arithmetic(obj, n):
    obj.operands = [env.a, env.cst(n, 8)]
    obj.type = type_data_processing


# ADD SP,n
@ispec("16<[ n(8) {e8} ]", mnemonic="ADD")
def mostek_ld(obj, n):
    disp = env.cst(n, 8).signextend(16)
    obj.operands = [env.sp, disp]
    obj.type = type_data_processing


# ------------------------------------------------
# General Purpose Arithmetic and CPU Control Group
# ------------------------------------------------


@ispec("8<[ {76} ]", mnemonic="HALT")
@ispec("8<[ {f3} ]", mnemonic="DI")
@ispec("8<[ {fb} ]", mnemonic="EI")
@ispec("8<[ {10} ]", mnemonic="STOP")
def mostek_gpa_cpuc(obj):
    obj.operands = []
    obj.type = type_cpu_state


@ispec("8<[ {27} ]", mnemonic="DAA")
@ispec("8<[ {2f} ]", mnemonic="CPL")
@ispec("8<[ {3f} ]", mnemonic="CCF")
@ispec("8<[ {37} ]", mnemonic="SCF")
@ispec("8<[ {00} ]", mnemonic="NOP")
def mostek_arithmetic(obj):
    obj.operands = []
    obj.type = type_data_processing


@ispec("8<[ {d9} ]", mnemonic="RETI")
def mostek_arithmetic(obj):
    obj.operands = []
    obj.type = type_control_flow


# -----------------------
# 16-bit Arithmetic Group
# -----------------------


@ispec(" 8<[ 00 ss(2) 1001 ]", mnemonic="ADD")
@ispec(" 8<[ 00 ss(2) 0011 ]", mnemonic="INC")
@ispec(" 8<[ 00 ss(2) 1011 ]", mnemonic="DEC")
def mostek_arithmetic(obj, ss):
    dst = getreg16(obj, 0b10)  # hl (or ix/iy)
    src = getreg16(obj, ss)
    obj.operands = [dst, src]
    if obj.mnemonic in ("INC", "DEC"):
        obj.operands.pop(0)
    obj.type = type_data_processing


@ispec("16<[ n(8) 1100 0110 ]", mnemonic="ADD")
@ispec("16<[ n(8) 1100 1110 ]", mnemonic="ADC")
@ispec("16<[ n(8) 1101 0110 ]", mnemonic="SUB")
@ispec("16<[ n(8) 1101 1110 ]", mnemonic="SBC")
@ispec("16<[ n(8) 1110 0110 ]", mnemonic="AND")
@ispec("16<[ n(8) 1111 0110 ]", mnemonic="OR")
@ispec("16<[ n(8) 1110 1110 ]", mnemonic="XOR")
@ispec("16<[ n(8) 1111 1110 ]", mnemonic="CP")
def mostek_arithmetic(obj, n):
    # DD/FD prefix are ignored
    obj.operands = [env.a, env.cst(n, 8)]
    obj.type = type_data_processing


# ----------------------
# Rotate and Shift Group
# ----------------------


@ispec("8<[ {07} ]", mnemonic="RLCA")
@ispec("8<[ {17} ]", mnemonic="RLA")
@ispec("8<[ {0f} ]", mnemonic="RRCA")
@ispec("8<[ {1f} ]", mnemonic="RRA")
def mostek_rotshift(obj):
    obj.operands = []
    obj.type = type_data_processing


@ispec("16<[ 00000 r(3) {cb} ]", mnemonic="RLC")
@ispec("16<[ 00010 r(3) {cb} ]", mnemonic="RL")
@ispec("16<[ 00001 r(3) {cb} ]", mnemonic="RRC")
@ispec("16<[ 00011 r(3) {cb} ]", mnemonic="RR")
@ispec("16<[ 00100 r(3) {cb} ]", mnemonic="SLA")
@ispec("16<[ 00110 r(3) {cb} ]", mnemonic="SWAP")  # undocumented
@ispec("16<[ 00101 r(3) {cb} ]", mnemonic="SRA")
@ispec("16<[ 00111 r(3) {cb} ]", mnemonic="SRL")
def mostek_rotshift(obj, r):
    if obj.misc["pfx"] is not None:
        raise InstructionError(obj)
    op1 = getreg8(obj, r)
    obj.operands = [op1]
    obj.type = type_data_processing


# -----------------------------
# Bit Set, Reset and Test Group
# -----------------------------

# unprefixed BIT & SET:
@ispec("16<[ 01 b(3) r(3) {cb} ]", mnemonic="BIT")
@ispec("16<[ 11 b(3) r(3) {cb} ]", mnemonic="SET")
def mostek_bitset(obj, b, r):
    if obj.misc["pfx"] is not None:
        raise InstructionError(obj)
    op1 = env.cst(b, 3)
    op2 = getreg8(obj, r)
    obj.operands = [op1, op2]
    obj.type = type_data_processing


# ----------
# Jump Group
# ----------


@ispec("24<[ nn(16) 11 000 011 ]", mnemonic="JP")
def mostek_jump(obj, nn):
    obj.operands = [env.cst(nn, 16)]
    obj.type = type_control_flow


@ispec("24<[ nn(16) 11 cc(3) 010 ]", mnemonic="JPcc")
def mostek_jump(obj, cc, nn):
    if cc >= 0b100:
        raise InstructionError(obj)
    obj.cond = env.CONDITION[cc]
    obj.operands = [obj.cond[0], env.cst(nn, 16)]
    obj.type = type_control_flow


@ispec("16<[ e(8) {18} ]", mnemonic="JR")
@ispec("16<[ e(8) {10} ]", mnemonic="DJNZ")
@ispec("16<[ e(8) {38} ]", mnemonic="JRcc", cond=("c", env.cf == 1))
@ispec("16<[ e(8) {30} ]", mnemonic="JRcc", cond=("nc", env.cf == 0))
@ispec("16<[ e(8) {28} ]", mnemonic="JRcc", cond=("z", env.zf == 1))
@ispec("16<[ e(8) {20} ]", mnemonic="JRcc", cond=("nz", env.zf == 0))
def mostek_jump(obj, e):
    disp = env.cst(e, 8).signextend(16)
    obj.operands = [disp]
    if hasattr(obj, "cond"):
        obj.operands.insert(0, obj.cond[0])
    obj.type = type_control_flow


@ispec("8<[ {e9} ]", mnemonic="JP")
def mostek_jump(obj):
    r = getreg16(obj, 0b10)
    # is it mem(r,16) ??
    obj.operands = [r]
    obj.type = type_control_flow


# ---------------------
# Call and Return Group
# ---------------------


@ispec("24<[ nn(16) 1100 1101 ]", mnemonic="CALL")
def mostek_call(obj, nn):
    obj.operands = [env.cst(nn, 16)]
    obj.type = type_control_flow


@ispec("24<[ nn(16) 11 cc(3) 100 ]", mnemonic="CALLcc")
def mostek_call(obj, cc, nn):
    if cc >= 0b100:
        raise InstructionError(obj)
    obj.cond = env.CONDITION[cc]
    obj.operands = [obj.cond[0], env.cst(nn, 16)]
    obj.type = type_control_flow


@ispec("8<[ {c9} ]", mnemonic="RET")
def mostek_ret(obj):
    obj.operands = []
    obj.type = type_control_flow


@ispec("8<[ 11 cc(3) 000 ]", mnemonic="RETcc")
def mostek_ret(obj, cc):
    if cc >= 0b100:
        raise InstructionError(obj)
    obj.cond = env.CONDITION[cc]
    obj.operands = [obj.cond[0]]
    obj.type = type_control_flow


@ispec("8<[ 11 t(3) 111 ]", mnemonic="RST")
def mostek_rst(obj, t):
    p = (0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38)[t]
    obj.operands = [env.cst(p, 8)]
    obj.type = type_control_flow
