# Copyright (c) 2007 The Hewlett-Packard Development Company
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
# not be construed as granting a license to any other intellectual
# property including but not limited to intellectual property relating
# to a hardware implementation of the functionality of the software
# licensed hereunder.  You may use the software subject to the license
# terms below provided that you ensure that this notice is replicated
# unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Gabe Black

microcode = '''

#
# Byte version of one operand unsigned multiply.
#

def macroop MUL_B_R
{
    mul1u rax, reg, flags=(OF,CF)
    mulel rax
    muleh ah
};

def macroop MUL_B_M
{
    ld t1, seg, sib, disp
    mul1u rax, t1, flags=(OF,CF)
    mulel rax
    muleh ah
};

def macroop MUL_B_P
{
    rdip t7
    ld t1, seg, riprel, disp
    mul1u rax, t1, flags=(OF,CF)
    mulel rax
    muleh ah
};

#
# One operand unsigned multiply.
#

def macroop MUL_R
{
    mul1u rax, reg, flags=(OF,CF)
    mulel rax
    muleh rdx
};

def macroop MUL_M
{
    ld t1, seg, sib, disp
    mul1u rax, t1, flags=(OF,CF)
    mulel rax
    muleh rdx
};

def macroop MUL_P
{
    rdip t7
    ld t1, seg, riprel, disp
    mul1u rax, t1, flags=(OF,CF)
    mulel rax
    muleh rdx
};

#
# Byte version of one operand signed multiply.
#

def macroop IMUL_B_R
{
    mul1s rax, reg, flags=(OF,CF)
    mulel rax
    muleh ah
};

def macroop IMUL_B_M
{
    ld t1, seg, sib, disp
    mul1s rax, t1, flags=(OF,CF)
    mulel rax
    muleh ah
};

def macroop IMUL_B_P
{
    rdip t7
    ld t1, seg, riprel, disp
    mul1s rax, t1, flags=(OF,CF)
    mulel rax
    muleh ah
};

#
# One operand signed multiply.
#

def macroop IMUL_R
{
    mul1s rax, reg, flags=(OF,CF)
    mulel rax
    muleh rdx
};

def macroop IMUL_M
{
    ld t1, seg, sib, disp
    mul1s rax, t1, flags=(OF,CF)
    mulel rax
    muleh rdx
};

def macroop IMUL_P
{
    rdip t7
    ld t1, seg, riprel, disp
    mul1s rax, t1, flags=(OF,CF)
    mulel rax
    muleh rdx
};

def macroop IMUL_R_R
{
    mul1s reg, regm, flags=(OF,CF)
    mulel reg
    muleh t0
};

def macroop IMUL_R_M
{
    ld t1, seg, sib, disp
    mul1s reg, t1, flags=(CF,OF)
    mulel reg
    muleh t0
};

def macroop IMUL_R_P
{
    rdip t7
    ld t1, seg, riprel, disp
    mul1s reg, t1, flags=(CF,OF)
    mulel reg
    muleh t0
};

#
# Three operand signed multiply.
#

def macroop IMUL_R_R_I
{
    limm t1, imm
    mul1s regm, t1, flags=(OF,CF)
    mulel reg
    muleh t0
};

def macroop IMUL_R_M_I
{
    limm t1, imm
    ld t2, seg, sib, disp
    mul1s t2, t1, flags=(OF,CF)
    mulel reg
    muleh t0
};

def macroop IMUL_R_P_I
{
    rdip t7
    limm t1, imm
    ld t2, seg, riprel, disp
    mul1s t2, t1, flags=(OF,CF)
    mulel reg
    muleh t0
};
'''

pcRel = '''
    rdip t7
    ld %s, seg, riprel, disp
'''
sibRel = '''
    ld %s, seg, sib, disp
'''

#
# One byte version of unsigned division
#

divcode = '''
def macroop DIV_B_%(suffix)s
{
    %(readOp1)s
    # Do the initial part of the division
    div1 ah, %(op1)s, dataSize=1

    #These are split out so we can initialize the number of bits in the
    #second register
    div2i t1, rax, 8, dataSize=1
    div2 t1, rax, t1, dataSize=1

    #Loop until we're out of bits to shift in
divLoopTop:
    div2 t1, rax, t1, dataSize=1
    div2 t1, rax, t1, flags=(EZF,), dataSize=1
    br label("divLoopTop"), flags=(nCEZF,)

    #Unload the answer
    divq rax, dataSize=1
    divr ah, dataSize=1
};
'''

#
# Unsigned division
#

divcode += '''
def macroop DIV_%(suffix)s
{
    %(readOp1)s
    # Do the initial part of the division
    div1 rdx, %(op1)s

    #These are split out so we can initialize the number of bits in the
    #second register
    div2i t1, rax, "env.dataSize * 8"
    div2 t1, rax, t1

    #Loop until we're out of bits to shift in
    #The amount of unrolling here could stand some tuning
divLoopTop:
    div2 t1, rax, t1
    div2 t1, rax, t1
    div2 t1, rax, t1
    div2 t1, rax, t1, flags=(EZF,)
    br label("divLoopTop"), flags=(nCEZF,)

    #Unload the answer
    divq rax
    divr rdx
};
'''

#
# One byte version of signed division
#

divcode += '''
def macroop IDIV_B_%(suffix)s
{
    # Negate dividend
    sub t1, t0, rax, flags=(ECF,), dataSize=1
    ruflag t4, 3
    sub t2, t0, ah, dataSize=1
    sub t2, t2, t4

    %(readOp1)s

    #Find the sign of the divisor
    slli t0, %(op1)s, 1, flags=(ECF,), dataSize=1

    # Negate divisor
    sub t3, t0, %(op1)s, dataSize=1
    # Put the divisor's absolute value into t3
    mov t3, t3, %(op1)s, flags=(nCECF,), dataSize=1

    #Find the sign of the dividend
    slli t0, ah, 1, flags=(ECF,), dataSize=1

    # Put the dividend's absolute value into t1 and t2
    mov t1, t1, rax, flags=(nCECF,), dataSize=1
    mov t2, t2, ah, flags=(nCECF,), dataSize=1

    # Do the initial part of the division
    div1 t2, t3, dataSize=1

    #These are split out so we can initialize the number of bits in the
    #second register
    div2i t4, t1, 8, dataSize=1
    div2 t4, t1, t4, dataSize=1

    #Loop until we're out of bits to shift in
divLoopTop:
    div2 t4, t1, t4, dataSize=1
    div2 t4, t1, t4, flags=(EZF,), dataSize=1
    br label("divLoopTop"), flags=(nCEZF,)

    #Unload the answer
    divq t5, dataSize=1
    divr t6, dataSize=1

    # Fix up signs. The sign of the dividend is still lying around in ECF.
    # The sign of the remainder, ah, is the same as the dividend. The sign
    # of the quotient is negated if the signs of the divisor and dividend
    # were different.

    # Negate the remainder
    sub t4, t0, t6, dataSize=1
    # If the dividend was negitive, put the negated remainder in ah.
    mov ah, ah, t4, (CECF,), dataSize=1
    # Otherwise put the regular remainder in ah.
    mov ah, ah, t6, (nCECF,), dataSize=1

    # Negate the quotient.
    sub t4, t0, t5, dataSize=1
    # If the dividend was negative, start using the negated quotient
    mov t5, t5, t4, (CECF,), dataSize=1

    # Check the sign of the divisor
    slli t0, %(op1)s, 1, flags=(ECF,), dataSize=1

    # Negate the (possibly already negated) quotient
    sub t4, t0, t5, dataSize=1
    # If the divisor was negative, put the negated quotient in rax.
    mov rax, rax, t4, (CECF,), dataSize=1
    # Otherwise put the one that wasn't negated (at least here) in rax.
    mov rax, rax, t5, (nCECF,), dataSize=1
};
'''

#
# Signed division
#

divcode += '''
def macroop IDIV_%(suffix)s
{
    # Negate dividend
    sub t1, t0, rax, flags=(ECF,)
    ruflag t4, 3
    sub t2, t0, rdx
    sub t2, t2, t4

    %(readOp1)s

    #Find the sign of the divisor
    slli t0, %(op1)s, 1, flags=(ECF,)

    # Negate divisor
    sub t3, t0, %(op1)s
    # Put the divisor's absolute value into t3
    mov t3, t3, %(op1)s, flags=(nCECF,)

    #Find the sign of the dividend
    slli t0, rdx, 1, flags=(ECF,)

    # Put the dividend's absolute value into t1 and t2
    mov t1, t1, rax, flags=(nCECF,)
    mov t2, t2, rdx, flags=(nCECF,)

    # Do the initial part of the division
    div1 t2, t3

    #These are split out so we can initialize the number of bits in the
    #second register
    div2i t4, t1, "env.dataSize * 8"
    div2 t4, t1, t4

    #Loop until we're out of bits to shift in
divLoopTop:
    div2 t4, t1, t4
    div2 t4, t1, t4
    div2 t4, t1, t4
    div2 t4, t1, t4, flags=(EZF,)
    br label("divLoopTop"), flags=(nCEZF,)

    #Unload the answer
    divq t5
    divr t6

    # Fix up signs. The sign of the dividend is still lying around in ECF.
    # The sign of the remainder, ah, is the same as the dividend. The sign
    # of the quotient is negated if the signs of the divisor and dividend
    # were different.

    # Negate the remainder
    sub t4, t0, t6
    # If the dividend was negitive, put the negated remainder in rdx.
    mov rdx, rdx, t4, (CECF,)
    # Otherwise put the regular remainder in rdx.
    mov rdx, rdx, t6, (nCECF,)

    # Negate the quotient.
    sub t4, t0, t5
    # If the dividend was negative, start using the negated quotient
    mov t5, t5, t4, (CECF,)

    # Check the sign of the divisor
    slli t0, %(op1)s, 1, flags=(ECF,)

    # Negate the (possibly already negated) quotient
    sub t4, t0, t5
    # If the divisor was negative, put the negated quotient in rax.
    mov rax, rax, t4, (CECF,)
    # Otherwise put the one that wasn't negated (at least here) in rax.
    mov rax, rax, t5, (nCECF,)
};
'''

microcode += divcode % {"suffix": "R",
                        "readOp1": "", "op1": "reg"}
microcode += divcode % {"suffix": "M",
                        "readOp1": sibRel % "t2", "op1": "t2"}
microcode += divcode % {"suffix": "P",
                        "readOp1": pcRel % "t2", "op1": "t2"}
