/*****************************************************************************
 *
 *   arm7.c
 *   Portable ARM7TDMI CPU Emulator
 *
 *   Copyright Steve Ellenoff, all rights reserved.
 *
 *   - This source code is released as freeware for non-commercial purposes.
 *   - You are free to use and redistribute this code in modified or
 *     unmodified form, provided you list me in the credits.
 *   - If you modify this source code, you must add a notice to each modified
 *     source file that it has been changed.  If you're a nice person, you
 *     will clearly mark each change too.  :)
 *   - If you wish to use this for commercial purposes, please contact me at
 *     sellenoff@hotmail.com
 *   - The author of this copywritten work reserves the right to change the
 *     terms of its usage and license at any time, including retroactively
 *   - This entire notice must remain in the source code.
 *
 *  This work is based on:
 *  #1) 'Atmel Corporation ARM7TDMI (Thumb) Datasheet - January 1999'
 *  #2) Arm 2/3/6 emulator By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino (MAME CORE 0.76)
 *  #3) Thumb support by Ryan Holtz
 *
 *****************************************************************************/

/******************************************************************************
 *  Notes:

    ** This is a plain vanilla implementation of an ARM7 cpu which incorporates my ARM7 core.
       It can be used as is, or used to demonstrate how to utilize the arm7 core to create a cpu
       that uses the core, since there are numerous different mcu packages that incorporate an arm7 core.

       See the notes in the arm7core.c file itself regarding issues/limitations of the arm7 core.
    **
*****************************************************************************/
#include "arm7.h"
#include "state.h"
#include "mamedbg.h"
#include "arm7core.h"   //include arm7 core

/* Example for showing how Co-Proc functions work */
#define TEST_COPROC_FUNCS 1

#define change_pc(x) 

/* prototypes */
#if TEST_COPROC_FUNCS
static WRITE32_HANDLER(test_do_callback);
static READ32_HANDLER(test_rt_r_callback);
static WRITE32_HANDLER(test_rt_w_callback);
static void test_dt_r_callback(UINT32 insn, UINT32 *prn, UINT32 (*read32)(UINT32 addr));
static void test_dt_w_callback(UINT32 insn, UINT32 *prn, void (*write32)(UINT32 addr, UINT32 data));
#ifdef MAME_DEBUG
static char *Spec_RT(char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0);
static char *Spec_DT(char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0);
static char *Spec_DO(char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0);
#endif
#endif

/* Macros that can be re-defined for custom cpu implementations - The core expects these to be defined */
/* In this case, we are using the default arm7 handlers (supplied by the core)
   - but simply changes these and define your own if needed for cpu implementation specific needs */
#define READ8(addr)         arm7_cpu_read8(addr)
#define WRITE8(addr,data)   arm7_cpu_write8(addr,data)
#define READ16(addr)        arm7_cpu_read16(addr)
#define WRITE16(addr,data)  arm7_cpu_write16(addr,data)
#define READ32(addr)        arm7_cpu_read32(addr)
#define WRITE32(addr,data)  arm7_cpu_write32(addr,data)
#define PTR_READ32          &arm7_cpu_read32
#define PTR_WRITE32         &arm7_cpu_write32

/* Macros that need to be defined according to the cpu implementation specific need */
#define ARM7REG(reg)        arm7.sArmRegister[reg]
#define ARM7                arm7
#define ARM7_ICOUNT         arm7_ICount

/* CPU Registers */
typedef struct
{
    ARM7CORE_REGS               // these must be included in your cpu specific register implementation
} ARM7_REGS;

static ARM7_REGS arm7;
int ARM7_ICOUNT;

/* include the arm7 core */
#include "arm7core.c"

/***************************************************************************
 * CPU SPECIFIC IMPLEMENTATIONS
 **************************************************************************/
void arm7_init(void)
{
    //must call core
    arm7_core_init("arm7");

#if TEST_COPROC_FUNCS
    // setup co-proc callbacks example
    arm7_coproc_do_callback = test_do_callback;
    arm7_coproc_rt_r_callback = test_rt_r_callback;
    arm7_coproc_rt_w_callback = test_rt_w_callback;
    arm7_coproc_dt_r_callback = test_dt_r_callback;
    arm7_coproc_dt_w_callback = test_dt_w_callback;
#ifdef MAME_DEBUG
    // setup dasm callbacks - direct method example
    arm7_dasm_cop_dt_callback = Spec_DT;
    arm7_dasm_cop_rt_callback = Spec_RT;
    arm7_dasm_cop_do_callback = Spec_DO;
#endif
#endif
}

void arm7_reset(void *param)
{
    // must call core reset
    arm7_core_reset(param);
}

void arm7_exit(void)
{
    /* nothing to do here */
}

int arm7_execute( int cycles )
{
/* include the arm7 core execute code */
#include "arm7exec.c"
}


void arm7_set_irq_line(int irqline, int state)
{
    // must call core
    arm7_core_set_irq_line(irqline,state);
}

unsigned arm7_get_context(void *dst)
{
	if(dst)
		memcpy(dst,&ARM7,sizeof(ARM7));
	return sizeof(ARM7);
}

void arm7_set_context(void *src)
{
	if(src)
		memcpy(&ARM7,src,sizeof(ARM7));
}

unsigned arm7_dasm(char *buffer, offs_t pc)
{
#ifdef MAME_DEBUG
    arm7_disasm( buffer, pc, READ32(pc));
    return 4;
#else
    sprintf(buffer, "$%08x", READ32(pc));
    return 4;
#endif
}

static data8_t arm7_reg_layout[] =
{
    -1,
    ARM7_R0,  ARM7_IR13, -1,
    ARM7_R1,  ARM7_IR14, -1,
    ARM7_R2,  ARM7_ISPSR, -1,
    ARM7_R3,  -1,
    ARM7_R4,  ARM7_FR8,  -1,
    ARM7_R5,  ARM7_FR9,  -1,
    ARM7_R6,  ARM7_FR10, -1,
    ARM7_R7,  ARM7_FR11, -1,
    ARM7_R8,  ARM7_FR12, -1,
    ARM7_R9,  ARM7_FR13, -1,
    ARM7_R10, ARM7_FR14, -1,
    ARM7_R11, ARM7_FSPSR, -1,
    ARM7_R12, -1,
    ARM7_R13, ARM7_AR13, -1,
    ARM7_R14, ARM7_AR14, -1,
    ARM7_R15, ARM7_ASPSR, -1,
    -1,
    ARM7_SR13, ARM7_UR13, -1,
    ARM7_SR14, ARM7_UR14, -1,
    ARM7_SSPSR, ARM7_USPSR, 0
};


static UINT8 arm7_win_layout[] = {
     0, 0,30,17,    /* register window (top rows) */
    31, 0,49,17,    /* disassembler window (left colums) */
     0,18,48, 4,    /* memory #1 window (right, upper middle) */
    49,18,31, 4,    /* memory #2 window (right, lower middle) */
     0,23,80, 1,    /* command line window (bottom rows) */
};

unsigned arm7_get_reg(int regnum)
{
	switch( regnum )
	{
        case REG_PREVIOUSPC:     return 0;    /* not implemented */
		case REG_PC:
        case ARM7_PC:            return R15;
        case REG_SP:             return GetRegister(13);
        /* registers shared by all operating modes */
        case ARM7_R0:            return ARM7REG( 0);
        case ARM7_R1:            return ARM7REG( 1);
        case ARM7_R2:            return ARM7REG( 2);
        case ARM7_R3:            return ARM7REG( 3);
        case ARM7_R4:            return ARM7REG( 4);
        case ARM7_R5:            return ARM7REG( 5);
        case ARM7_R6:            return ARM7REG( 6);
        case ARM7_R7:            return ARM7REG( 7);
        case ARM7_R8:            return ARM7REG( 8);
        case ARM7_R9:            return ARM7REG( 9);
        case ARM7_R10:           return ARM7REG(10);
        case ARM7_R11:           return ARM7REG(11);
        case ARM7_R12:           return ARM7REG(12);
        case ARM7_R13:           return ARM7REG(13);
        case ARM7_R14:           return ARM7REG(14);
        case ARM7_R15:           return ARM7REG(15);


        /* FIRQ Mode Shadowed Registers */
        case ARM7_FR8:           return ARM7REG(eR8_FIQ);
        case ARM7_FR9:           return ARM7REG(eR9_FIQ);
        case ARM7_FR10:          return ARM7REG(eR10_FIQ);
        case ARM7_FR11:          return ARM7REG(eR11_FIQ);
        case ARM7_FR12:          return ARM7REG(eR12_FIQ);
        case ARM7_FR13:          return ARM7REG(eR13_FIQ);
        case ARM7_FR14:          return ARM7REG(eR14_FIQ);
        case ARM7_FSPSR:         return ARM7REG(eSPSR_FIQ);

        /* IRQ Mode Shadowed Registers */
        case ARM7_IR13:          return ARM7REG(eR13_IRQ);
        case ARM7_IR14:          return ARM7REG(eR14_IRQ);
        case ARM7_ISPSR:         return ARM7REG(eSPSR_IRQ);

        /* Supervisor Mode Shadowed Registers */
        case ARM7_SR13:          return ARM7REG(eR13_SVC);
        case ARM7_SR14:          return ARM7REG(eR14_SVC);
        case ARM7_SSPSR:         return ARM7REG(eSPSR_SVC);

        /* Abort Mode Shadowed Registers */
        case ARM7_AR13:          return ARM7REG(eR13_ABT);
        case ARM7_AR14:          return ARM7REG(eR14_ABT);
        case ARM7_ASPSR:         return ARM7REG(eSPSR_ABT);

        /* Undefined Mode Shadowed Registers */
        case ARM7_UR13:          return ARM7REG(eR13_UND);
        case ARM7_UR14:          return ARM7REG(eR14_UND);
        case ARM7_USPSR:         return ARM7REG(eSPSR_UND);
	}
	return 0;
}

void arm7_set_reg (int regnum, unsigned val)
{
	switch( regnum )
	{
        case REG_PC:
        case ARM7_PC:    R15 = val;                          break;
        case REG_SP:     SetRegister(13,val);                break;

        /* registers shared by all operating modes */
        case ARM7_R0:    ARM7REG( 0) = val;                  break;
        case ARM7_R1:    ARM7REG( 1) = val;                  break;
        case ARM7_R2:    ARM7REG( 2) = val;                  break;
        case ARM7_R3:    ARM7REG( 3) = val;                  break;
        case ARM7_R4:    ARM7REG( 4) = val;                  break;
        case ARM7_R5:    ARM7REG( 5) = val;                  break;
        case ARM7_R6:    ARM7REG( 6) = val;                  break;
        case ARM7_R7:    ARM7REG( 7) = val;                  break;
        case ARM7_R8:    ARM7REG( 8) = val;                  break;
        case ARM7_R9:    ARM7REG( 9) = val;                  break;
        case ARM7_R10:   ARM7REG(10) = val;                  break;
        case ARM7_R11:   ARM7REG(11) = val;                  break;
        case ARM7_R12:   ARM7REG(12) = val;                  break;
        case ARM7_R13:   ARM7REG(13) = val;                  break;
        case ARM7_R14:   ARM7REG(14) = val;                  break;
        case ARM7_R15:   ARM7REG(15) = val;                  break;
        case ARM7_CPSR:  SET_CPSR(val);                      break;

        /* FIRQ Mode Shadowed Registers */
        case ARM7_FR8:   ARM7REG(eR8_FIQ)  = val;            break;
        case ARM7_FR9:   ARM7REG(eR9_FIQ)  = val;            break;
        case ARM7_FR10:  ARM7REG(eR10_FIQ) = val;            break;
        case ARM7_FR11:  ARM7REG(eR11_FIQ) = val;            break;
        case ARM7_FR12:  ARM7REG(eR12_FIQ) = val;            break;
        case ARM7_FR13:  ARM7REG(eR13_FIQ) = val;            break;
        case ARM7_FR14:  ARM7REG(eR14_FIQ) = val;            break;
        case ARM7_FSPSR: ARM7REG(eSPSR_FIQ) = val;           break;

        /* IRQ Mode Shadowed Registers */
        case ARM7_IR13:  ARM7REG(eR13_IRQ) = val;            break;
        case ARM7_IR14:  ARM7REG(eR14_IRQ) = val;            break;
        case ARM7_ISPSR: ARM7REG(eSPSR_IRQ) = val;           break;

        /* Supervisor Mode Shadowed Registers */
        case ARM7_SR13:  ARM7REG(eR13_SVC) = val;            break;
        case ARM7_SR14:  ARM7REG(eR14_SVC) = val;            break;
        case ARM7_SSPSR: ARM7REG(eSPSR_SVC) = val;           break;

        /* Abort Mode Shadowed Registers */
        case ARM7_AR13:  ARM7REG(eR13_ABT) = val;            break;
        case ARM7_AR14:  ARM7REG(eR14_ABT) = val;            break;
        case ARM7_ASPSR: ARM7REG(eSPSR_ABT) = val;           break;

        /* Undefined Mode Shadowed Registers */
        case ARM7_UR13:  ARM7REG(eR13_UND) = val;            break;
        case ARM7_UR14:  ARM7REG(eR14_UND) = val;            break;
        case ARM7_USPSR: ARM7REG(eSPSR_UND) = val;           break;
	}
}

void arm7_set_irq_callback(int (*callback)(int irqline))
{
	ARM7.irq_callback = callback;
}


/**************************************************************************
 * Generic set_info
 **************************************************************************/

/**************************************************************************
 * Generic get_info
 **************************************************************************/

const char *arm7_info(void *context, int regnum)
{
	static char buffer[8][32+1];
	static int which = 0;

	which = (which + 1) % 8;
	buffer[which][0] = '\0';

	switch( regnum )
	{
        /* registers shared by all operating modes */
        case CPU_INFO_REG + ARM7_PC: sprintf( buffer[which], "PC  :%08x", R15 );  break;
        case CPU_INFO_REG + ARM7_R0: sprintf( buffer[which], "R0  :%08x", ARM7REG( 0) );  break;
        case CPU_INFO_REG + ARM7_R1: sprintf( buffer[which], "R1  :%08x", ARM7REG( 1) );  break;
        case CPU_INFO_REG + ARM7_R2: sprintf( buffer[which], "R2  :%08x", ARM7REG( 2) );  break;
        case CPU_INFO_REG + ARM7_R3: sprintf( buffer[which], "R3  :%08x", ARM7REG( 3) );  break;
        case CPU_INFO_REG + ARM7_R4: sprintf( buffer[which], "R4  :%08x", ARM7REG( 4) );  break;
        case CPU_INFO_REG + ARM7_R5: sprintf( buffer[which], "R5  :%08x", ARM7REG( 5) );  break;
        case CPU_INFO_REG + ARM7_R6: sprintf( buffer[which], "R6  :%08x", ARM7REG( 6) );  break;
        case CPU_INFO_REG + ARM7_R7: sprintf( buffer[which], "R7  :%08x", ARM7REG( 7) );  break;
        case CPU_INFO_REG + ARM7_R8: sprintf( buffer[which], "R8  :%08x", ARM7REG( 8) );  break;
        case CPU_INFO_REG + ARM7_R9: sprintf( buffer[which], "R9  :%08x", ARM7REG( 9) );  break;
        case CPU_INFO_REG + ARM7_R10:sprintf( buffer[which], "R10 :%08x", ARM7REG(10) );  break;
        case CPU_INFO_REG + ARM7_R11:sprintf( buffer[which], "R11 :%08x", ARM7REG(11) );  break;
        case CPU_INFO_REG + ARM7_R12:sprintf( buffer[which], "R12 :%08x", ARM7REG(12) );  break;
        case CPU_INFO_REG + ARM7_R13:sprintf( buffer[which], "R13 :%08x", ARM7REG(13) );  break;
        case CPU_INFO_REG + ARM7_R14:sprintf( buffer[which], "R14 :%08x", ARM7REG(14) );  break;
        case CPU_INFO_REG + ARM7_R15:sprintf( buffer[which], "R15 :%08x", ARM7REG(15) );  break;

        /* FIRQ Mode Shadowed Registers */
        case CPU_INFO_REG + ARM7_FR8: sprintf( buffer[which], "FR8 :%08x", ARM7REG(eR8_FIQ) );   break;
        case CPU_INFO_REG + ARM7_FR9: sprintf( buffer[which], "FR9 :%08x", ARM7REG(eR9_FIQ) );   break;
        case CPU_INFO_REG + ARM7_FR10:sprintf( buffer[which], "FR10:%08x", ARM7REG(eR10_FIQ) );  break;
        case CPU_INFO_REG + ARM7_FR11:sprintf( buffer[which], "FR11:%08x", ARM7REG(eR11_FIQ));   break;
        case CPU_INFO_REG + ARM7_FR12:sprintf( buffer[which], "FR12:%08x", ARM7REG(eR12_FIQ) );  break;
        case CPU_INFO_REG + ARM7_FR13:sprintf( buffer[which], "FR13:%08x", ARM7REG(eR13_FIQ) );  break;
        case CPU_INFO_REG + ARM7_FR14:sprintf( buffer[which], "FR14:%08x", ARM7REG(eR14_FIQ) );  break;
        case CPU_INFO_REG + ARM7_FSPSR:sprintf(buffer[which], "FR16:%08x", ARM7REG(eSPSR_FIQ) ); break;

        /* IRQ Mode Shadowed Registers */
        case CPU_INFO_REG + ARM7_IR13:sprintf( buffer[which], "IR13:%08x", ARM7REG(eR13_IRQ) );  break;
        case CPU_INFO_REG + ARM7_IR14:sprintf( buffer[which], "IR14:%08x", ARM7REG(eR14_IRQ) );  break;
        case CPU_INFO_REG + ARM7_ISPSR:sprintf(buffer[which], "IR16:%08x", ARM7REG(eSPSR_IRQ) ); break;

        /* Supervisor Mode Shadowed Registers */
        case CPU_INFO_REG + ARM7_SR13:sprintf( buffer[which], "SR13:%08x", ARM7REG(eR13_SVC) );  break;
        case CPU_INFO_REG + ARM7_SR14:sprintf( buffer[which], "SR14:%08x", ARM7REG(eR14_SVC) );  break;
        case CPU_INFO_REG + ARM7_SSPSR:sprintf(buffer[which], "SR16:%08x", ARM7REG(eSPSR_SVC) ); break;

        /* Abort Mode Shadowed Registers */
        case CPU_INFO_REG + ARM7_AR13:sprintf( buffer[which], "AR13:%08x", ARM7REG(eR13_ABT) );  break;
        case CPU_INFO_REG + ARM7_AR14:sprintf( buffer[which], "AR14:%08x", ARM7REG(eR14_ABT) );  break;
        case CPU_INFO_REG + ARM7_ASPSR:sprintf(buffer[which], "AR16:%08x", ARM7REG(eSPSR_ABT) ); break;

        /* Undefined Mode Shadowed Registers */
        case CPU_INFO_REG + ARM7_UR13:sprintf( buffer[which], "UR13:%08x", ARM7REG(eR13_UND) );  break;
        case CPU_INFO_REG + ARM7_UR14:sprintf( buffer[which], "UR14:%08x", ARM7REG(eR14_UND) );  break;
        case CPU_INFO_REG + ARM7_USPSR:sprintf(buffer[which], "UR16:%08x", ARM7REG(eSPSR_UND) ); break;
        case CPU_INFO_FLAGS:
            sprintf(buffer[which], "%c%c%c%c%c%c%c %s",
                (ARM7REG(eCPSR) & N_MASK) ? 'N' : '-',
                (ARM7REG(eCPSR) & Z_MASK) ? 'Z' : '-',
                (ARM7REG(eCPSR) & C_MASK) ? 'C' : '-',
                (ARM7REG(eCPSR) & V_MASK) ? 'V' : '-',
                (ARM7REG(eCPSR) & I_MASK) ? 'I' : '-',
                (ARM7REG(eCPSR) & F_MASK) ? 'F' : '-',
                (ARM7REG(eCPSR) & T_MASK) ? 'T' : '-',
                GetModeText(ARM7REG(eCPSR)));
        break;
        case CPU_INFO_NAME:       return "ARM7";
        case CPU_INFO_FAMILY:     return "Acorn Risc Machine";
        case CPU_INFO_VERSION:    return "1.3";
        case CPU_INFO_FILE:       return __FILE__;
        case CPU_INFO_CREDITS:    return "Copyright 2004-2006 Steve Ellenoff, sellenoff@hotmail.com";
        case CPU_INFO_REG_LAYOUT: return (const char*)arm7_reg_layout;
        case CPU_INFO_WIN_LAYOUT: return (const char*)arm7_win_layout;
	}
	return buffer[which];
}

/* TEST COPROC CALLBACK HANDLERS - Used for example on how to implement only */
#if TEST_COPROC_FUNCS

static WRITE32_HANDLER(test_do_callback)
{
    LOG(("test_do_callback opcode=%x, =%x\n", offset, data));
}
static READ32_HANDLER(test_rt_r_callback)
{
    UINT32 data=0;
    LOG(("test_rt_r_callback opcode=%x\n", offset));
    return data;
}
static WRITE32_HANDLER(test_rt_w_callback)
{
    LOG(("test_rt_w_callback opcode=%x, data from ARM7 register=%x\n", offset, data));
}
static void test_dt_r_callback(UINT32 insn, UINT32 *prn, UINT32 (*read32)(UINT32 addr))
{
    LOG(("test_dt_r_callback: insn = %x\n", insn));
}
static void test_dt_w_callback(UINT32 insn, UINT32 *prn, void (*write32)(UINT32 addr, UINT32 data))
{
    LOG(("test_dt_w_callback: opcode = %x\n", insn));
}

/* Custom Co-proc DASM handlers */
#ifdef ENABLE_DEBUGGER
static char *Spec_RT(char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0)
{
    pBuf += sprintf(pBuf, "SPECRT");
    return pBuf;
}
static char *Spec_DT(char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0)
{
    pBuf += sprintf(pBuf, "SPECDT");
    return pBuf;
}
static char *Spec_DO(char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0)
{
    pBuf += sprintf(pBuf, "SPECDO");
    return pBuf;
}
#endif
#endif
