/**
 * TrueReality - N64/mnemonic.h
 * Copyright (C) 1998, 1999 Niki W. Waibel
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 * Information about me (the author):
 *   Niki W. Waibel, Reichenau 20, 6890 Lustenau, Austria - EUROPE
 *   niki.waibel@gmx.net
**/





#define __MNEMONIC_H





#ifndef __TYPE_SIZES_H
        #include <type_sizes.h>
#endif

#ifndef __FILE_LOADER_EXTERN_H
        #include <file_loader_extern.h>
#endif

#include <ansi_esc_codes.h>





/**********************\
*                      *
* instruction reader   *
*                      *
\**********************/


#define READ_CPU_INSTRUCTION(addr)                                                                  \
(                                                                                              \
        ( (addr < 0xa4000040) )                                                                \
        ?                                                                                      \
                ( *((WORD *)(mem.rd_ram + (addr & 0x1fffffff))) )     /* instr in prgcode */   \
        :                                                                                      \
                ( (addr < 0xe0000000) )                                                        \
                ?                                                                              \
                        ( *(WORD *)(mem.sp_dmem + (addr & 0xfff)) )   /* instr in bootcode */  \
                :                                                                              \
                        ( *(WORD *)(rom.image + (addr & 0xfff)) )              /* for test */  \
)





/**********************\
*                      *
* mnemonic definitions *
*                      *
\**********************/

#define __CODE          (reg.code = READ_CPU_INSTRUCTION(reg.pc))
#define __NEXT_CODE     (READ_CPU_INSTRUCTION((reg.pc + 4)))
#define __PREV_CODE     (READ_CPU_INSTRUCTION((reg.pc - 4)))

#define __OPCODE        ((BYTE)(__CODE >> 26))

#define __RS            (((BYTE)(reg.code >> 21)) & 0x1f)

#define __RT            (((BYTE)(reg.code >> 16)) & 0x1f)

#define __RD            (((BYTE)(reg.code >> 11)) & 0x1f)

#define __SA            (((BYTE)(reg.code >>  6)) & 0x1f)

#define __F             ( (BYTE)(reg.code)       & 0x3f)

#define __I             ( (sWORD)(sHWORD)reg.code )
#define __O             ( reg.pc + 4 + (__I << 2) )

#define ____T           (reg.code & 0x3ffffff)
#define __T             ( (reg.pc & 0xf0000000) | (____T << 2) )





/******************************\
*                              *
* mnemonic definitions for fpu *
*                              *
\******************************/

#define FMT_S   16      /* 32bit binary floating-point */
#define FMT_D   17      /* 64bit binary floating-point */
#define FMT_W   20      /* 32bit binary fixed-point    */
#define FMT_L   21      /* 64bit binary fixed-point    */


#define __FMT   __RS

#define __FT    __RT

#define __FS    __RD

#define __FD    __SA

#define FT                                                              \
(                                                                       \
        (reg.cpr[0][12] & 0x04000000) ?                                 \
                reg.cpr[1][__FT]                                        \
        :                                                               \
                ((reg.cpr[1][__FT+1] << 32) | (reg.cpr[1][__FT] & 0xffffffff))       \
)

#define FD                                                              \
(                                                                       \
        (reg.cpr[0][12] & 0x04000000) ?                                 \
                reg.cpr[1][__FD]                                        \
        :                                                               \
                ((reg.cpr[1][__FD+1] << 32) | (reg.cpr[1][__FD] & 0xffffffff))       \
)


#define FS                                                              \
(                                                                       \
        (reg.cpr[0][12] & 0x04000000) ?                                 \
                reg.cpr[1][__FS]                                        \
        :                                                               \
                ((reg.cpr[1][__FS+1] << 32) | (reg.cpr[1][__FS] & 0xffffffff))       \
)


#define STORE_FPR(value)                                                \
{                                                                       \
        if(reg.cpr[0][12] & 0x04000000)                                 \
        {                                                               \
                (reg.cpr[1][__FD] = value);                             \
        }                                                               \
        else                                                            \
        {                                                               \
                (reg.cpr[1][__FD+0] = (sDWORD)(sWORD)value);            \
                (reg.cpr[1][__FD+1] = (sDWORD)(sWORD)(value >> 32));    \
        }                                                               \
}

        




/**********************\
*                      *
* mnemonic debugging   *
*                      *
\**********************/

#ifdef DEBUG

        #define PRINT_NOTHING

        #define PRINT_O         printf(" $%08lx", __O);
        #define PRINT_RD        printf(" r%02u", __RD);
        #define PRINT_RS        printf(" r%02u", __RS);
        #define PRINT_T         printf(" $%08lx", __T); PRINT_SYMBOL(__T)

        #define PRINT_RD_RS     printf(" r%02u, r%02u", __RD, __RS);
        #define PRINT_RS_I      printf(" r%02u, $%08lx (%ld)", __RS, (sWORD)__I, (sWORD)__I);
        #define PRINT_RS_O      printf(" r%02u, $%08lx", __RS, __O);
        #define PRINT_RS_RD     printf(" r%02u, r%02u", __RS, __RD);
        #define PRINT_RS_RT     printf(" r%02u, r%02u", __RS, __RT);
        #define PRINT_RT_I      printf(" r%02u, $%04hx ($%08lx)", __RT, (HWORD)__I, (sWORD)(__I << 16));
        #define PRINT_RT_RD     printf(" r%02u, r%02u", __RT, __RD);

        #define PRINT_RD_RS_RT  printf(" r%02u, r%02u, r%02u", __RD, __RS, __RT);
        #define PRINT_RD_RT_SA  printf(" r%02u, r%02u, r%02u", __RD, __RT, __SA);
        #define PRINT_RD_RT_RS  printf(" r%02u, r%02u, r%02u", __RD, __RT, __RS);
        #define PRINT_RS_RT_O   printf(" r%02u, r%02u, $%08lx", __RS, __RT, __O);
        #define PRINT_RT_O_B    printf(" r%02u, $%04lx(r%02u)", __RT, __I, __RS);
        #define PRINT_RT_RS_I   printf(" r%02u, r%02u, $%04hx (%hd)", __RT, __RS, (sHWORD)__I, (sHWORD)__I);

        #define PRINT_FD_FS_FT  printf(" f%02u, f%02u, f%02u", __FD, __FS, __FT);
        #define PRINT_FD_FS     printf(" f%02u, f%02u", __FD, __FS);
        #define PRINT_FT_O_B    printf(" f%02u, $%04lx(r%02u)", __FT, __I, __RS);
        #define PRINT_RT_FS     printf(" r%02u, f%02u", __RT, __FS);

        #define PRINT_C0        printf(" r%02u, cpr0[%02u]", __RT, __RD);
        #define PRINT_C0C       printf(" r%02u, ccr0[%02u]", __RT, __RD);
        #define PRINT_RT_C1     printf(" r%02u, ccr1[%02u]", __RT, __FS);
        #define PRINT_C2_O_B    printf(" cpr2[%02u], $%04lx(r%02u)", __RT, __I, __RS);

#else /* #ifndef DEBUG */

        #define PRINT_NOTHING

        #define PRINT_O
        #define PRINT_RD
        #define PRINT_RS
        #define PRINT_T

        #define PRINT_RD_RS
        #define PRINT_RS_I
        #define PRINT_RS_O
        #define PRINT_RS_RD
        #define PRINT_RS_RT
        #define PRINT_RT_I
        #define PRINT_RT_RD

        #define PRINT_RD_RS_RT
        #define PRINT_RD_RT_SA
        #define PRINT_RD_RT_RS
        #define PRINT_RS_RT_O
        #define PRINT_RT_O_B
        #define PRINT_RT_RS_I

        #define PRINT_FD_FS_FT
        #define PRINT_FD_FS
        #define PRINT_FT_O_B
        #define PRINT_RT_FS

        #define PRINT_C0
        #define PRINT_C0C
        #define PRINT_RT_C1
        #define PRINT_C2_O_B

#endif /* #ifndef DEBUG */






#define PRINT_FMT               \
        switch(__FMT)           \
        {                       \
            case FMT_S:         \
                putchar('s');   \
                break;          \
            case FMT_D:         \
                putchar('d');   \
                break;          \
            case FMT_W:         \
                putchar('w');   \
                break;          \
            case FMT_L:         \
                putchar('l');   \
                break;          \
            default:            \
                putchar('?');   \
        }





#ifdef DEBUG_NOTIMPLEMENTED
        #define PRINT_NOT_IMPLEMENTED(x)        \
                puts(x" - NOT IMPLEMENTED");    \
                fflush(stdout);
#else
        #define PRINT_NOT_IMPLEMENTED(x)
#endif





#ifdef DEBUG_XINST
        #define PRINT_XINST(x)                  \
                if(prefs.debug & DBG_CPU_INSTR) \
                {                               \
                        puts(x); fflush(stdout);\
                }
#else
        #define PRINT_XINST(x)
#endif





#ifdef DEBUG
        #define PRINT_DEBUG_HEAD                                        \
                if(prefs.debug & DBG_CPU_COUNT)                         \
                        printf("CPU(%08llx): ", reg.cpr[0][COUNT]);     \
                else                                                    \
                        fputs("CPU: ", stdout);                         \
                printf("[$%08lx]: ", reg.pc);                           \
                printf("$%08lx: ", reg.code);

        #define PRINT_DEBUG_FOOT                                        \
                if(prefs.debug & DBG_CPU_REG)                           \
                        cpu_print_gpr();
#else
        #define PRINT_DEBUG_HEAD
        #define PRINT_DEBUG_FOOT
#endif





#ifdef DEBUG
        #define PRINT_DEBUGGING_INFO(name, format)              \
                if(prefs.debug & DBG_CPU_INSTR)                 \
                {                                               \
                        PRINT_DEBUG_HEAD                        \
                        printf("%-10s", name);                  \
                        PRINT_##format                          \
                        putchar('\n');                          \
                        PRINT_DEBUG_FOOT                        \
                                                                \
                }
#else
        #define PRINT_DEBUGGING_INFO(name, format)
#endif





#ifdef DEBUG
        #define PRINT_DEBUGGING_INFO_FPU(name, format)  \
                if(prefs.debug & DBG_CPU_INSTR)         \
                {                                       \
                        int i;                          \
                                                        \
                        PRINT_DEBUG_HEAD                \
                        printf(" %s", name);            \
                        PRINT_FMT                       \
                        for(i=0; i<9-strlen(name); i++) \
                                putchar(' ');           \
                        PRINT_##format                  \
                        putchar('\n');                  \
                        PRINT_DEBUG_FOOT                \
                }
#else
        #define PRINT_DEBUGGING_INFO_FPU(name, format)
#endif





#ifdef DEBUG
        #define PRINT_DEBUGGING_INFO_FPU_COND                   \
                if(prefs.debug & DBG_CPU_INSTR)                 \
                {                                               \
                        PRINT_DEBUG_HEAD                        \
                                                                \
                        switch(__F)                             \
                        {                                       \
                            int i;                              \
                                                                \
                            case 0x30:                          \
                                fputs(" C.f.", stdout);         \
                                PRINT_FMT                       \
                                for(i=0; i<5; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x31:                          \
                                fputs(" C.un.", stdout);        \
                                PRINT_FMT                       \
                                for(i=0; i<4; i++)              \
                                        putchar(' ');           \
                               break;                           \
                            case 0x32:                          \
                                fputs(" C.eq.", stdout);        \
                                PRINT_FMT                       \
                                for(i=0; i<4; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x33:                          \
                                fputs(" C.ueq.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x34:                          \
                                fputs(" C.olt.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x35:                          \
                                fputs(" C.ult.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x36:                          \
                                fputs(" C.ole.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x37:                          \
                                fputs(" C.ule.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x38:                          \
                                fputs(" C.sf.", stdout);        \
                                PRINT_FMT                       \
                                for(i=0; i<4; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x39:                          \
                                fputs(" C.ngle.", stdout);      \
                                PRINT_FMT                       \
                                for(i=0; i<2; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x3a:                          \
                                fputs(" C.seq.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x3b:                          \
                                fputs(" C.ngl.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x3c:                          \
                                fputs(" C.lt.", stdout);        \
                                PRINT_FMT                       \
                                for(i=0; i<4; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x3d:                          \
                                fputs(" C.nge.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x3e:                          \
                                fputs(" C.le.", stdout);        \
                                PRINT_FMT                       \
                                for(i=0; i<4; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            case 0x3f:                          \
                                fputs(" C.ngt.", stdout);       \
                                PRINT_FMT                       \
                                for(i=0; i<3; i++)              \
                                        putchar(' ');           \
                                break;                          \
                            default:                            \
                                fputs(" C.?.", stdout);         \
                                PRINT_FMT                       \
                                for(i=0; i<5; i++)              \
                                        putchar(' ');           \
                        }                                       \
                        PRINT_FD_FS                             \
                        putchar('\n');                          \
                        PRINT_DEBUG_FOOT                        \
                }
#else
        #define PRINT_DEBUGGING_INFO_FPU_COND
#endif





#ifdef HALT
        #define CPU_HALT reg.halt = 1; \
                         puts(ST_FG_RED "CPU halted!" ST_FG_DEF); fflush(stdout);
#else
        #define CPU_HALT
#endif





#define PRINT_SYMBOL(a)                                                 \
{                                                                       \
        int     i;                                                      \
                                                                        \
        for(i=0; prefs.symbols==USE_SYMBOLS && i<prefs.n_symbols; i++)  \
        {                                                               \
                if(((prefs.symtab)[i]).addr == a)                       \
                {                                                       \
                        printf(" (%s)", ((prefs.symtab)[i]).name);      \
                }                                                       \
        }                                                               \
}
