# (c) Electronic Arts. All Rights Reserved.

###########################################################################
# This script is used to generate constant values for the math library
# on some of the supported platforms.  It outputs an assembly source file
# to stdout.
###########################################################################

###########################################################################
# Utility Functions for building an assembly file.
###########################################################################
def OutputHeader():
    print """# (c) Electronic Arts. All Rights Reserved.

# The following source code was machine generated by constants.py.
# Please modify and rerun constants.py to add/modify constants.
"""

def OutputSectionBeginning( section ):
    print '.section %s\n' % section

# For some more info on name mangling for C++ using GCC take a look at:
#   http://en.wikipedia.org/wiki/Name_mangling
#
# This function currently lets you name mangle variables.  But, it will
# not mangle more complex signatures such as functions/methods.
def MangleName( name ):
    result = '_ZN'
    components = name.split('::')
    for component in components:
        result = result + "%d%s" % ( len(component), component )
    result = result + 'E'
    return result

def OutputVector( name, valueTuple ):
    mangledName = MangleName(name)
    print """#
# Readonly data for %s
#
    .global %s
    .align  4
    .type   %s, @object
    .size   %s, 16
%s:
    .long   0x%08x, 0x%08x, 0x%08x, 0x%08x

    """ % ( name, mangledName, mangledName, mangledName, mangledName, valueTuple[0], valueTuple[1], valueTuple[2], valueTuple[3] )

def OutputFloatVector( name, valueTuple ):
    mangledName = MangleName(name)
    print """#
# Readonly data for %s
#
    .global %s
    .align  4
    .type   %s, @object
    .size   %s, 16
%s:
    .float   %.20f, %.20f, %.20f, %.20f

    """ % ( name, mangledName, mangledName, mangledName, mangledName, valueTuple[0], valueTuple[1], valueTuple[2], valueTuple[3] )

def OutputVectorArray( name, valueTupleList ):
    mangledName = MangleName(name)
    print """#
# Readonly data for %s[%d]
#
    .global %s
    .align  4
    .type   %s, @object
    .size   %s, %d
%s:""" % ( name, len(valueTupleList), mangledName, mangledName, mangledName, 16 * len(valueTupleList), mangledName)
    for valueTuple in valueTupleList:
        print '    .long   0x%08x, 0x%08x, 0x%08x, 0x%08x' % ( valueTuple[0], valueTuple[1], valueTuple[2], valueTuple[3] )
    print


###########################################################################
# Code and constants for generating complex constants
###########################################################################

X0 = 0
Y0 = 1
Z0 = 2
W0 = 3
X1 = 4
Y1 = 5
Z1 = 6
W1 = 7
BYTE_00 = 32
BYTE_FF = 48
BYTE_80 = 56

def GenerateShiftLeftConstant( shiftAmount ):
    return (
        0x00010203 + 0x01010101 * ( 0 + (shiftAmount)),
        0x00010203 + 0x01010101 * ( 4 + (shiftAmount)),
        0x00010203 + 0x01010101 * ( 8 + (shiftAmount)),
        0x00010203 + 0x01010101 * (12 + (shiftAmount))
        )

def ElementSelectWordConstant( index ):
    return 0x00010203 + 0x01010101 * (index * 4)

def ElementSelectHalfConstant( index ):
    return 0x0001 + 0x0101 * (index * 2)

# This function excepts either 4, 8 or 16 arguments
# Arguments are indices used to form permution constants.  If 4 indices are
# specified, a vector that selects word elements will be generated. If 8
# indices are specified, a vector that selects half word elements will be
# generated.  If 8 indices are specified, a vector that selects byte elements
# will be generated.
def VectorPermuteConstant( *indices ):
    if len(indices) == 4:
        return tuple( map(ElementSelectWordConstant,indices) )
    if len(indices) == 8:
        # Generate half words
        vectorWords = map(ElementSelectHalfConstant, indices )
        resultList = []
        for i in range(4):
            resultList.append( (vectorWords[2*i] << 16) | vectorWords[2*i+1] )
        return tuple(resultList)
    if len(indices) == 16:
        # Generate bytes
        resultList = []
        for i in range(4):
            resultList.append( (indices[4*i] << 24) | indices[4*i+1] << 16 | indices[4*i+2] << 8 | indices[4*i+3] )
        return tuple(resultList)

def GetByteInVector( vec, index ):
    def GetByteInInt32( int32, index ):
        # return the index'th byte (0 being the index of the MSB)
        return 0xff & (int32 >> (8*(3-index)))
    elementIndex = index / 4
    indexInElement = index % 4
    return GetByteInInt32( vec[elementIndex], indexInElement )

def GetByteInVectors( vectors, index ):
    vectorIndex = index / 16
    byteIndexInVector = index % 16
    return GetByteInVector( vectors[vectorIndex], byteIndexInVector )

def SetByteInVector( vec, index, byte ):
    def SetByteInInt32( int32, index, byte ):
        clearMask = 0xffffffff ^ (0xff << (8*(3-index)))
        return (int32 & clearMask) | (byte << (8*(3-index)))
    elements = list(vec)
    elementIndex = index / 4
    indexInElement = index % 4
    elements[elementIndex] = SetByteInInt32( elements[elementIndex], indexInElement, byte)
    return tuple(elements)

def VectorPermute( vecA, vecB, permuteVec ):
    result = (0, 0, 0, 0)
    for i in range(16):
        readIndex = GetByteInVector( permuteVec, i )
        byteValue = GetByteInVectors( (vecA, vecB), readIndex )
        result = SetByteInVector( result, i, byteValue )
    return result

def PrintVector(vec):
    print '( 0x%08x, 0x%08x, 0x%08x, 0x%08x )' % (vec[0], vec[1], vec[2], vec[3])

###########################################################################
# Misc constants
###########################################################################
reverseElementsInVector = VectorPermuteConstant(3, 2, 1, 0)

swizzleConstants = [ 
    # dest = X, src = X
    VectorPermuteConstant(X1, Y0, Z0, W0),
    # dest = X, src = Y
    VectorPermuteConstant(Y1, Y0, Z0, W0),
    # dest = X, src = Z
    VectorPermuteConstant(Z1, Y0, Z0, W0),
    # dest = X, src = W
    VectorPermuteConstant(W1, Y0, Z0, W0),
    # dest = Y, src = X
    VectorPermuteConstant(X0, X1, Z0, W0),
    # dest = Y, src = Y
    VectorPermuteConstant(X0, Y1, Z0, W0),
    # dest = Y, src = Z
    VectorPermuteConstant(X0, Z1, Z0, W0),
    # dest = Y, src = W
    VectorPermuteConstant(X0, W1, Z0, W0),
    # dest = Z, src = X
    VectorPermuteConstant(X0, Y0, X1, W0),
    # dest = Z, src = Y
    VectorPermuteConstant(X0, Y0, Y1, W0),
    # dest = Z, src = Z
    VectorPermuteConstant(X0, Y0, Z1, W0),
    # dest = Z, src = W
    VectorPermuteConstant(X0, Y0, W1, W0),
    # dest = W, src = X
    VectorPermuteConstant(X0, Y0, Z0, X1),
    # dest = W, src = Y
    VectorPermuteConstant(X0, Y0, Z0, Y1),
    # dest = W, src = Z
    VectorPermuteConstant(X0, Y0, Z0, Z1),
    # dest = W, src = W
    VectorPermuteConstant(X0, Y0, Z0, W1)
]

store2Table0 = [     
    VectorPermuteConstant(X0, Y0, Z1, W1),    
    VectorPermuteConstant(X1, X0, Y0, W1),    
    VectorPermuteConstant(X1, Y1, X0, Y0),    
    VectorPermuteConstant(X1, Y1, Z1, X0)
]

store2Table1 = [     
    VectorPermuteConstant(X1, Y1, Z1, W1),
    VectorPermuteConstant(X1, Y1, Z1, W1),
    VectorPermuteConstant(X1, Y1, Z1, W1),    
    VectorPermuteConstant(Y0, Y1, Z1, W1),
]

store3Table0 = [     
    VectorPermuteConstant(X0, Y0, Z0, W1),    
    VectorPermuteConstant(X1, X0, Y0, Z0),    
    VectorPermuteConstant(X1, Y1, X0, Y0),    
    VectorPermuteConstant(X1, Y1, Z1, X0)
]

store3Table1 = [     
    VectorPermuteConstant(X1, Y1, Z1, W1),
    VectorPermuteConstant(X1, Y1, Z1, W1),
    VectorPermuteConstant(Z0, Y1, Z1, W1),    
    VectorPermuteConstant(Y0, Z0, Z1, W1),
]

# platform constants
PLATFORM_SPU, PLATFORM_PPU = range(2)

###########################################################################
# Code to generate the necessary constants file
###########################################################################
def OutputConstants(platform):
    OutputHeader()
    OutputSectionBeginning('.rodata')
    OutputVector( 'rw::math::vpu::detail::gQuatFromMat_xSigns', ( 0x00000000, 0x00000000, 0x80000000, 0x80000000 ) )
    OutputVector( 'rw::math::vpu::detail::gQuatFromMat_ySigns', ( 0x00000000, 0x80000000, 0x00000000, 0x80000000 ) )
    OutputVector( 'rw::math::vpu::detail::gQuatFromMat_zSigns', ( 0x00000000, 0x80000000, 0x80000000, 0x00000000 ) )
    OutputVector( 'rw::math::vpu::detail::gSignMaskXYZ',        ( 0x80000000, 0x80000000, 0x80000000, 0x00000000 ) )
    OutputVector( 'rw::math::vpu::detail::gCrossProductPermuteConstant',    VectorPermuteConstant(Y0, Z0, X0, W0) )

    OutputVectorArray( 'rw::math::vpu::detail::gSwizzleStoreConstants', swizzleConstants )

    boolConstants = [ 4 * (0,), 4 * (0xffffffff,) ]
    OutputVectorArray( 'rw::math::vpu::detail::gBooleanConstants', boolConstants )

    OutputVector( 'rw::math::vpu::detail::gGatherMostSignificantBytesV3',  4 * (0x00040808,) )
    OutputVector( 'rw::math::vpu::detail::gGatherMostSignificantBytesV4',  4 * (0x0004080c,) )

    if platform == PLATFORM_SPU:
        OutputVector( 'rw::math::vpu::detail::gSignMaskXZ',         ( 0x80000000, 0x00000000, 0x80000000, 0x00000000 ) )
        OutputVector( 'rw::math::vpu::detail::gSignMaskYW',         ( 0x00000000, 0x80000000, 0x00000000, 0x80000000 ) )
        OutputVector( 'rw::math::vpu::detail::gExponentAndMantissa',( 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff ) )
        OutputVector( 'rw::math::vpu::detail::gMergeHighPermuteConstant',       VectorPermuteConstant(X0, X1, Y0, Y1) )
        OutputVector( 'rw::math::vpu::detail::gMergeLowPermuteConstant',        VectorPermuteConstant(Z0, Z1, W0, W1) )
        OutputVector( 'rw::math::vpu::detail::gMergeHighBytePermuteConstant', VectorPermuteConstant(0,16,1,17,2,18,3,19,4,20,5,21,6,22,7,23) )
        OutputVector( 'rw::math::vpu::detail::gMergeLowBytePermuteConstant',  VectorPermuteConstant(8,24,9,25,10,26,11,27,12,28,13,29,14,30,15, 31) )
        OutputVector( 'rw::math::vpu::detail::gMergeHighHalfPermuteConstant', VectorPermuteConstant(0,8,1,9,2,10,3,11) )
        OutputVector( 'rw::math::vpu::detail::gMergeLowHalfPermuteConstant', VectorPermuteConstant(4,12,5,13,6,14,7,15) )

        shiftLeftValues = [ GenerateShiftLeftConstant(i) for i in range(17) ]
        OutputVectorArray( 'rw::math::vpu::detail::gShiftLeftDoubleConstants', shiftLeftValues )

        splatVectors = [ VectorPermuteConstant( i, i, i, i ) for i in range(4) ]
        OutputVectorArray( 'rw::math::vpu::detail::gSplatElementPermuteConstants', splatVectors )
        
        OutputVectorArray( 'rw::math::vpu::detail::gStore2Table0', store2Table0 ) 
        OutputVectorArray( 'rw::math::vpu::detail::gStore2Table1', store2Table1 ) 
        OutputVectorArray( 'rw::math::vpu::detail::gStore3Table0', store3Table0 ) 
        OutputVectorArray( 'rw::math::vpu::detail::gStore3Table1', store3Table1 ) 

    elif platform == PLATFORM_PPU:
        OutputVector( 'rw::math::vpu::detail::gXYZMask',            ( 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000 ) )

import sys
usageMessage = "Specify either 'spu' or 'ppu' as a command line paramater."
if len(sys.argv) != 2:
    print usageMessage
else:
    platform = sys.argv[1]
    if platform == 'spu':
        OutputConstants(PLATFORM_SPU)
    elif platform == 'ppu':
        OutputConstants(PLATFORM_PPU)
    else:
        print usageMessage
