/* libs/pixelflinger/codeflinger/x86/GGLX86Assembler.h
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/


#ifndef ANDROID_GGLX86ASSEMBLER_H
#define ANDROID_GGLX86ASSEMBLER_H

#include <stdint.h>
#include <sys/types.h>

#include <private/pixelflinger/ggl_context.h>

#include "codeflinger/x86/X86Assembler.h"


namespace android {

// ----------------------------------------------------------------------------

#define CONTEXT_LOAD(REG, FIELD) \
    MOV_MEM_TO_REG(GGL_OFFSETOF(FIELD), mBuilderContext.Rctx, REG)

#define CONTEXT_STORE(REG, FIELD) \
    MOV_REG_TO_MEM(REG, GGL_OFFSETOF(FIELD), mBuilderContext.Rctx)

class X86RegisterAllocator
{
public:
    class RegisterFile;

    RegisterFile&   registerFile();
    int             reserveReg(int reg);
    int             obtainReg();
    void            recycleReg(int reg);
    void            reset();

    class RegisterFile
    {
    public:
                            RegisterFile();
                            RegisterFile(const RegisterFile& rhs);
                            ~RegisterFile();

                void        reset();

                bool operator == (const RegisterFile& rhs) const;
                bool operator != (const RegisterFile& rhs) const {
                    return !operator == (rhs);
                }

                int         reserve(int reg);
                void        reserveSeveral(uint32_t regMask);

                void        recycle(int reg);
                void        recycleSeveral(uint32_t regMask);

                int         obtain();
        inline  int         isUsed(int reg) const;

                bool        hasFreeRegs() const;
                int         countFreeRegs() const;

                uint32_t    touched() const;
        inline  uint32_t    status() const { return mStatus; }

        enum {
            OUT_OF_REGISTERS = 0x1
        };

    private:
        uint32_t    mRegs;
        uint32_t    mTouched;
        uint32_t    mStatus;
    };

    class Scratch
    {
    public:
            Scratch(RegisterFile& regFile)
                : mRegFile(regFile), mScratch(0) {
            }
            ~Scratch() {
                mRegFile.recycleSeveral(mScratch);
            }
        int obtain() {
            int reg = mRegFile.obtain();
            mScratch |= 1<<reg;
            return reg;
        }
        void reserve(int reg) {
            mRegFile.reserve(reg);
            mScratch |= 1<<reg;
        }
        void recycle(int reg) {
            mRegFile.recycle(reg);
            mScratch &= ~(1<<reg);
        }
        bool isUsed(int reg) {
            return (mScratch & (1<<reg));
        }
        int countFreeRegs() {
            return mRegFile.countFreeRegs();
        }
    private:
        RegisterFile&   mRegFile;
        uint32_t        mScratch;
    };

/*
// currently we don't use it

    class Spill
    {
    public:
        Spill(RegisterFile& regFile, X86Assembler& gen, uint32_t reglist)
            : mRegFile(regFile), mGen(gen), mRegList(reglist), mCount(0)
        {
            if (reglist) {
                int count = 0;
                while (reglist) {
                    count++;
                    reglist &= ~(1 << (31 - __builtin_clz(reglist)));
                }
                if (count == 1) {
                    int reg = 31 - __builtin_clz(mRegList);
                    // move to the stack
                } else {
                    // move to the stack
                }
                mRegFile.recycleSeveral(mRegList);
                mCount = count;
            }
        }
        ~Spill() {
            if (mRegList) {
                if (mCount == 1) {
                    int reg = 31 - __builtin_clz(mRegList);
                    // move to the stack
                } else {
                }
                mRegFile.reserveSeveral(mRegList);
            }
        }
    private:
        RegisterFile&           mRegFile;
        X86Assembler&           mGen;
        uint32_t                mRegList;
        int                     mCount;
    };
*/

private:
    RegisterFile    mRegs;
};

// ----------------------------------------------------------------------------

class GGLX86Assembler : public X86Assembler, public X86RegisterAllocator
{
public:

            GGLX86Assembler(const sp<Assembly>& assembly);
            ~GGLX86Assembler();

    char*   base() const { return 0; } // XXX
    char*   pc() const { return 0; } // XXX

    void    reset(int opt_level);


        // generate scanline code for given needs
    int     scanline(const needs_t& needs, context_t const* c);
    int     scanline_core(const needs_t& needs, context_t const* c);

    enum {
        CLEAR_LO    = 0x0001,
        CLEAR_HI    = 0x0002,
        CORRUPTIBLE = 0x0004,
        FIRST       = 0x0008
    };

    enum { //load/store flags
        WRITE_BACK  = 0x0001
    };

    struct reg_t {
        reg_t() : reg(-1), flags(0), offset_ebp(0) {
        }
        reg_t(int r, int f=0, int offset=0)
            : reg(r), flags(f), offset_ebp(offset) {
        }
        void setTo(int r, int f=0, int offset=0) {
            reg=r; flags=f; offset_ebp=offset;
        }
        int         reg;
        uint16_t    flags;
        int         offset_ebp;
    };

    struct integer_t : public reg_t {
        integer_t() : reg_t(), s(0) {
        }
        integer_t(int r, int sz=32, int f=0, int offset=0)
            : reg_t(r, f, offset), s(sz) {
        }
        void setTo(int r, int sz=32, int f=0, int offset=0) {
            reg_t::setTo(r, f, offset); s=sz;
        }
        int8_t s;
        inline int size() const { return s; }
    };

    struct pixel_t : public reg_t {
        pixel_t() : reg_t() {
            memset(&format, 0, sizeof(GGLFormat));
        }
        pixel_t(int r, const GGLFormat* fmt, int f=0, int offset=0)
            : reg_t(r, f, offset), format(*fmt) {
        }
        void setTo(int r, const GGLFormat* fmt, int f=0, int offset=0) {
            reg_t::setTo(r, f, offset); format = *fmt;
        }
        GGLFormat format;
        inline int hi(int c) const { return format.c[c].h; }
        inline int low(int c) const { return format.c[c].l; }
        inline int mask(int c) const { return ((1<<size(c))-1) << low(c); }
        inline int size() const { return format.size*8; }
        inline int size(int c) const { return component_size(c); }
        inline int component_size(int c) const { return hi(c) - low(c); }
    };

    struct component_t : public reg_t {
        component_t() : reg_t(), h(0), l(0) {
        }
        component_t(int r, int f=0, int offset=0)
            : reg_t(r, f, offset), h(0), l(0) {
        }
        component_t(int r, int lo, int hi, int f=0, int offset=0)
            : reg_t(r, f, offset), h(hi), l(lo) {
        }
        explicit component_t(const integer_t& rhs)
            : reg_t(rhs.reg, rhs.flags, rhs.offset_ebp), h(rhs.s), l(0) {
        }
        explicit component_t(const pixel_t& rhs, int component) {
            setTo(  rhs.reg,
                    rhs.format.c[component].l,
                    rhs.format.c[component].h,
                    rhs.flags|CLEAR_LO|CLEAR_HI, rhs.offset_ebp);
        }
        void setTo(int r, int lo=0, int hi=0, int f=0, int offset=0) {
            reg_t::setTo(r, f, offset); h=hi; l=lo;
        }
        int8_t h;
        int8_t l;
        inline int size() const { return h-l; }
    };

    struct pointer_t : public reg_t {
        pointer_t() : reg_t(), size(0) {
        }
        pointer_t(int r, int s, int f=0, int offset=0)
            : reg_t(r, f, offset), size(s) {
        }
        void setTo(int r, int s, int f=0, int offset=0) {
            reg_t::setTo(r, f, offset); size=s;
        }
        int8_t size;
    };


private:
    struct tex_coord_t {
        reg_t       s;
        reg_t       t;
        pointer_t   ptr;
    };

    struct fragment_parts_t {
        uint32_t    packed  : 1;
        uint32_t    reload  : 2;
        uint32_t    iterated_packed  : 1;
        pixel_t     iterated;
        pointer_t   cbPtr;
        pointer_t   covPtr;
        reg_t       count;
        reg_t       argb[4];
        reg_t       argb_dx[4];
        reg_t       z;
        reg_t       dither;
        pixel_t     texel[GGL_TEXTURE_UNIT_COUNT];
        tex_coord_t coords[GGL_TEXTURE_UNIT_COUNT];
    };

    struct texture_unit_t {
        int         format_idx;
        GGLFormat   format;
        int         bits;
        int         swrap;
        int         twrap;
        int         env;
        int         pot;
        int         linear;
        uint8_t     mask;
        uint8_t     replaced;
    };

    struct texture_machine_t {
        texture_unit_t  tmu[GGL_TEXTURE_UNIT_COUNT];
        uint8_t         mask;
        uint8_t         replaced;
        uint8_t         directTexture;
        uint8_t         activeUnits;
    };

    struct component_info_t {
        bool    masked      : 1;
        bool    inDest      : 1;
        bool    needed      : 1;
        bool    replaced    : 1;
        bool    iterated    : 1;
        bool    smooth      : 1;
        bool    blend       : 1;
        bool    fog         : 1;
    };

    struct builder_context_t {
        context_t const*    c;
        needs_t             needs;
        int                 Rctx;
    };

    template <typename T>
    void modify(T& r, Scratch& regs)
    {
        if (!(r.flags & CORRUPTIBLE)) {
            r.reg = regs.obtain();
            r.flags |= CORRUPTIBLE;
        }
    }

    // helpers
    void    base_offset(pointer_t& d, pointer_t& b, const reg_t& o);

    // texture environement
    void    modulate(   component_t& dest,
                        const component_t& incoming,
                        const pixel_t& texel, int component);

    void    decal(  component_t& dest,
                    const component_t& incoming,
                    const pixel_t& texel, int component);

    void    blend(  component_t& dest,
                    const component_t& incoming,
                    const pixel_t& texel, int component, int tmu);

    void    add(  component_t& dest,
                    const component_t& incoming,
                    const pixel_t& texel, int component);

    // load/store stuff
    void    store(const pointer_t& addr, const pixel_t& src, uint32_t flags=0);
    void    load(pointer_t& addr, const pixel_t& dest, uint32_t flags=0);

    void    extract(integer_t& d, const pixel_t& s, int component);
    void    extract(component_t& d, const pixel_t& s, int component);
    void    extract(integer_t& d, int s, int h, int l, int bits=32);
    void    expand(integer_t& d, const integer_t& s, int dbits);
    void    expand(integer_t& d, const component_t& s, int dbits);
    void    expand(component_t& d, const component_t& s, int dbits);
    void    downshift(pixel_t& d, int component, component_t s, reg_t& dither);


    void    mul_factor( component_t& d,
                        const integer_t& v,
                        const integer_t& f, Scratch& scratches);

    void    mul_factor_add( component_t& d,
                            const integer_t& v,
                            const integer_t& f,
                            const component_t& a);

    void    component_add(  component_t& d,
                            const integer_t& dst,
                            const integer_t& src);

    void    component_sat(  const component_t& v, const int temp_reg);


    void    build_scanline_preparation(fragment_parts_t& parts,
                                    const needs_t& needs);

    void    build_smooth_shade(fragment_parts_t& parts);

    void    build_component(    pixel_t& pixel,
                                fragment_parts_t& parts,
                                int component,
                                Scratch& global_scratches);

    void    build_incoming_component(
                                component_t& temp,
                                int dst_size,
                                fragment_parts_t& parts,
                                int component,
                                Scratch& scratches,
                                Scratch& global_scratches);

    void    init_iterated_color(fragment_parts_t& parts, const reg_t& x);

    void    build_iterated_color(   component_t& fragment,
                                    fragment_parts_t& parts,
                                    int component,
                                    Scratch& regs);

    void    decodeLogicOpNeeds(const needs_t& needs);

    void    decodeTMUNeeds(const needs_t& needs, context_t const* c);

    void    init_textures(  tex_coord_t* coords,
                            const reg_t& x,
                            const reg_t& y);

    void    build_textures( fragment_parts_t& parts,
                            Scratch& regs);

    void    filter8(   const fragment_parts_t& parts,
                        pixel_t& texel, const texture_unit_t& tmu,
                        reg_t reg_U, reg_t reg_V, pointer_t& txPtr,
                        int FRAC_BITS, Scratch& scratches);

    void    filter16(   const fragment_parts_t& parts,
                        pixel_t& texel, const texture_unit_t& tmu,
                        reg_t reg_U, reg_t reg_V, pointer_t& txPtr,
                        int FRAC_BITS, Scratch& scratches);

    void    filter24(   const fragment_parts_t& parts,
                        pixel_t& texel, const texture_unit_t& tmu,
                        int U, int V, pointer_t& txPtr,
                        int FRAC_BITS);

    void    filter32(   const fragment_parts_t& parts,
                        pixel_t& texel, const texture_unit_t& tmu,
                        reg_t reg_U, reg_t reg_V, pointer_t& txPtr,
                        int FRAC_BITS, Scratch& scratches);

    void    build_texture_environment(  component_t& fragment,
                                        fragment_parts_t& parts,
                                        int component,
                                        Scratch& regs);

    void    wrapping(   int d,
                        int coord, int size,
                        int tx_wrap, int tx_linear, Scratch& scratches);

    void    build_fog(  component_t& temp,
                        int component,
                        Scratch& parent_scratches);

    void    build_blending(     component_t& in_out,
                                pixel_t& pixel,
                                int component,
                                Scratch& parent_scratches);

    void    build_blend_factor(
                integer_t& factor, int f, int component,
                const pixel_t& dst_pixel,
                integer_t& fragment,
                integer_t& fb,
                Scratch& scratches);

    void    build_blendFOneMinusF(  component_t& temp,
                                    const integer_t& factor,
                                    const integer_t& fragment,
                                    const integer_t& fb);

    void    build_blendOneMinusFF(  component_t& temp,
                                    const integer_t& factor,
                                    const integer_t& fragment,
                                    const integer_t& fb);

    void build_coverage_application(component_t& fragment,
                                    fragment_parts_t& parts,
                                    Scratch& regs);

    void build_alpha_test(component_t& fragment, const fragment_parts_t& parts);

    enum { Z_TEST=1, Z_WRITE=2 };
    void build_depth_test(const fragment_parts_t& parts, uint32_t mask);
    void build_iterate_z(const fragment_parts_t& parts);
    void build_iterate_f(const fragment_parts_t& parts);
    void build_iterate_texture_coordinates(const fragment_parts_t& parts);

    void build_logic_op(pixel_t& pixel, Scratch& regs);

    void build_masking(pixel_t& pixel, Scratch& regs);

    void build_and_immediate(int d, int s, uint32_t mask, int bits);

    bool    isAlphaSourceNeeded() const;

    enum {
        FACTOR_SRC=1, FACTOR_DST=2, BLEND_SRC=4, BLEND_DST=8
    };

    enum {
        LOGIC_OP=1, LOGIC_OP_SRC=2, LOGIC_OP_DST=4
    };

    static int blending_codes(int fs, int fd);

    builder_context_t   mBuilderContext;
    texture_machine_t   mTextureMachine;
    component_info_t    mInfo[4];
    int                 mBlending;
    int                 mMasking;
    int                 mAllMasked;
    int                 mLogicOp;
    int                 mAlphaTest;
    int                 mAA;
    int                 mDithering;
    int                 mDepthTest;

    int             mSmooth;
    int             mFog;
    pixel_t         mDstPixel;

    GGLFormat       mCbFormat;

    int             mBlendFactorCached;
    integer_t       mAlphaSource;

    int             mBaseRegister;

    int             mBlendSrc;
    int             mBlendDst;
    int             mBlendSrcA;
    int             mBlendDstA;

    int             mOptLevel;

    // to stretch esp and shrink esp
    int             mCurSp;
};

// ----------------------------------------------------------------------------

}; // namespace android

#endif // ANDROID_GGLX86ASSEMBLER_H
