mirror of
https://github.com/holub/mame
synced 2025-05-29 09:03:08 +03:00

state_save_combine_module_and_tag() function in favor of passing the tag when registering. Revisited all save state item registrations and changed them to use the tag where appropriate.
1881 lines
64 KiB
C
1881 lines
64 KiB
C
/***************************************************************************
|
|
|
|
ppccom.c
|
|
|
|
Common PowerPC definitions and functions
|
|
|
|
***************************************************************************/
|
|
|
|
#include "ppccom.h"
|
|
#include "cpuexec.h"
|
|
#include "mame.h"
|
|
#include "deprecat.h"
|
|
|
|
|
|
/***************************************************************************
|
|
DEBUGGING
|
|
***************************************************************************/
|
|
|
|
#define PRINTF_SPU (0)
|
|
#define PRINTF_DECREMENTER (0)
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
FUNCTION PROTOTYPES
|
|
***************************************************************************/
|
|
|
|
static TIMER_CALLBACK( ppc4xx_fit_callback );
|
|
static TIMER_CALLBACK( ppc4xx_pit_callback );
|
|
static TIMER_CALLBACK( ppc4xx_spu_callback );
|
|
static TIMER_CALLBACK( decrementer_int_callback );
|
|
|
|
static void ppc4xx_set_irq_line(powerpc_state *ppc, UINT32 bitmask, int state);
|
|
|
|
static void ppc4xx_dma_update_irq_states(powerpc_state *ppc);
|
|
static int ppc4xx_dma_fetch_transmit_byte(powerpc_state *ppc, int dmachan, UINT8 *byte);
|
|
static int ppc4xx_dma_handle_receive_byte(powerpc_state *ppc, int dmachan, UINT8 byte);
|
|
static void ppc4xx_dma_exec(powerpc_state *ppc, int dmachan);
|
|
|
|
static void ppc4xx_spu_update_irq_states(powerpc_state *ppc);
|
|
static void ppc4xx_spu_timer_reset(powerpc_state *ppc);
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INLINE FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
page_access_allowed - return true if we are
|
|
allowed to access memory based on the type
|
|
of access and the protection bits
|
|
-------------------------------------------------*/
|
|
|
|
INLINE int page_access_allowed(int transtype, UINT8 key, UINT8 protbits)
|
|
{
|
|
if (key == 0)
|
|
return (transtype == TRANSLATE_WRITE) ? (protbits != 3) : TRUE;
|
|
else
|
|
return (transtype == TRANSLATE_WRITE) ? (protbits == 2) : (protbits != 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
get_cr - return the current CR value
|
|
-------------------------------------------------*/
|
|
|
|
INLINE UINT32 get_cr(powerpc_state *ppc)
|
|
{
|
|
return ((ppc->cr[0] & 0x0f) << 28) |
|
|
((ppc->cr[1] & 0x0f) << 24) |
|
|
((ppc->cr[2] & 0x0f) << 20) |
|
|
((ppc->cr[3] & 0x0f) << 16) |
|
|
((ppc->cr[4] & 0x0f) << 12) |
|
|
((ppc->cr[5] & 0x0f) << 8) |
|
|
((ppc->cr[6] & 0x0f) << 4) |
|
|
((ppc->cr[7] & 0x0f) << 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
set_cr - set the current CR value
|
|
-------------------------------------------------*/
|
|
|
|
INLINE void set_cr(powerpc_state *ppc, UINT32 value)
|
|
{
|
|
ppc->cr[0] = value >> 28;
|
|
ppc->cr[1] = value >> 24;
|
|
ppc->cr[2] = value >> 20;
|
|
ppc->cr[3] = value >> 16;
|
|
ppc->cr[4] = value >> 12;
|
|
ppc->cr[5] = value >> 8;
|
|
ppc->cr[6] = value >> 4;
|
|
ppc->cr[7] = value >> 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
get_xer - return the current XER value
|
|
-------------------------------------------------*/
|
|
|
|
INLINE UINT32 get_xer(powerpc_state *ppc)
|
|
{
|
|
return ppc->spr[SPR_XER] | (ppc->xerso << 31);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
set_xer - set the current XER value
|
|
-------------------------------------------------*/
|
|
|
|
INLINE void set_xer(powerpc_state *ppc, UINT32 value)
|
|
{
|
|
ppc->spr[SPR_XER] = value & ~XER_SO;
|
|
ppc->xerso = value >> 31;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
get_timebase - return the current timebase
|
|
value
|
|
-------------------------------------------------*/
|
|
|
|
INLINE UINT64 get_timebase(powerpc_state *ppc)
|
|
{
|
|
return (cpu_get_total_cycles(Machine->cpu[ppc->cpunum]) - ppc->tb_zero_cycles) / ppc->tb_divisor;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
set_timebase - set the timebase
|
|
-------------------------------------------------*/
|
|
|
|
INLINE void set_timebase(powerpc_state *ppc, UINT64 newtb)
|
|
{
|
|
ppc->tb_zero_cycles = cpu_get_total_cycles(Machine->activecpu) - newtb * ppc->tb_divisor;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
get_decremeter - return the current
|
|
decrementer value
|
|
-------------------------------------------------*/
|
|
|
|
INLINE UINT32 get_decrementer(powerpc_state *ppc)
|
|
{
|
|
INT64 cycles_until_zero = ppc->dec_zero_cycles - cpu_get_total_cycles(Machine->cpu[ppc->cpunum]);
|
|
cycles_until_zero = MAX(cycles_until_zero, 0);
|
|
return cycles_until_zero / ppc->tb_divisor;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
set_decrementer - set the decremeter
|
|
-------------------------------------------------*/
|
|
|
|
INLINE void set_decrementer(powerpc_state *ppc, UINT32 newdec)
|
|
{
|
|
UINT64 cycles_until_done = ((UINT64)newdec + 1) * ppc->tb_divisor;
|
|
UINT32 curdec = get_decrementer(ppc);
|
|
|
|
if (PRINTF_DECREMENTER)
|
|
{
|
|
UINT64 total = cpu_get_total_cycles(Machine->cpu[ppc->cpunum]);
|
|
mame_printf_debug("set_decrementer: olddec=%08X newdec=%08X divisor=%d totalcyc=%08X%08X timer=%08X%08X\n",
|
|
curdec, newdec, ppc->tb_divisor,
|
|
(UINT32)(total >> 32), (UINT32)total, (UINT32)(cycles_until_done >> 32), (UINT32)cycles_until_done);
|
|
}
|
|
|
|
ppc->dec_zero_cycles = cpu_get_total_cycles(Machine->cpu[ppc->cpunum]) + cycles_until_done;
|
|
timer_adjust_oneshot(ppc->decrementer_int_timer, ATTOTIME_IN_CYCLES(cycles_until_done, ppc->cpunum), 0);
|
|
|
|
if ((INT32)curdec >= 0 && (INT32)newdec < 0)
|
|
ppc->irq_pending |= 0x02;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INITIALIZATION AND SHUTDOWN
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_init - initialize the powerpc_state
|
|
structure based on the configured type
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_init(powerpc_state *ppc, powerpc_flavor flavor, UINT8 cap, int tb_divisor, const device_config *device, int index, int clock, cpu_irq_callback irqcallback)
|
|
{
|
|
const powerpc_config *config = device->static_config;
|
|
|
|
/* initialize based on the config */
|
|
memset(ppc, 0, sizeof(*ppc));
|
|
ppc->cpunum = cpunum_get_active();
|
|
ppc->flavor = flavor;
|
|
ppc->cap = cap;
|
|
ppc->cache_line_size = 32;
|
|
ppc->tb_divisor = tb_divisor;
|
|
ppc->cpu_clock = clock;
|
|
ppc->irq_callback = irqcallback;
|
|
ppc->device = device;
|
|
ppc->system_clock = (config != NULL) ? config->bus_frequency : clock;
|
|
ppc->tb_divisor = (ppc->tb_divisor * clock + ppc->system_clock / 2 - 1) / ppc->system_clock;
|
|
|
|
/* allocate the virtual TLB */
|
|
ppc->vtlb = vtlb_alloc(device, ADDRESS_SPACE_PROGRAM, (cap & PPCCAP_603_MMU) ? PPC603_FIXED_TLB_ENTRIES : 0, POWERPC_TLB_ENTRIES);
|
|
|
|
/* allocate a timer for the compare interrupt */
|
|
if (cap & PPCCAP_OEA)
|
|
ppc->decrementer_int_timer = timer_alloc(decrementer_int_callback, ppc);
|
|
|
|
/* and for the 4XX interrupts if needed */
|
|
if (cap & PPCCAP_4XX)
|
|
{
|
|
ppc->fit_timer = timer_alloc(ppc4xx_fit_callback, ppc);
|
|
ppc->pit_timer = timer_alloc(ppc4xx_pit_callback, ppc);
|
|
ppc->spu.timer = timer_alloc(ppc4xx_spu_callback, ppc);
|
|
}
|
|
|
|
/* reset the state */
|
|
ppccom_reset(ppc);
|
|
|
|
/* register for save states */
|
|
state_save_register_item("ppc", device->tag, 0, ppc->pc);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->r);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->f);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->cr);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->xerso);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->fpscr);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->msr);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->sr);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->spr);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->dcr);
|
|
if (cap & PPCCAP_4XX)
|
|
{
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->spu.regs);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->spu.txbuf);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->spu.rxbuf);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->spu.rxbuffer);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->spu.rxin);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->spu.rxout);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->pit_reload);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->irqstate);
|
|
}
|
|
if (cap & PPCCAP_603_MMU)
|
|
{
|
|
state_save_register_item("ppc", device->tag, 0, ppc->mmu603_cmp);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->mmu603_hash);
|
|
state_save_register_item_array("ppc", device->tag, 0, ppc->mmu603_r);
|
|
}
|
|
state_save_register_item("ppc", device->tag, 0, ppc->irq_pending);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->tb_zero_cycles);
|
|
state_save_register_item("ppc", device->tag, 0, ppc->dec_zero_cycles);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_exit - common cleanup/exit
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_exit(powerpc_state *ppc)
|
|
{
|
|
if (ppc->vtlb != NULL)
|
|
vtlb_free(ppc->vtlb);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_reset - reset the state of all the
|
|
registers
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_reset(powerpc_state *ppc)
|
|
{
|
|
int tlbindex;
|
|
|
|
/* initialize the OEA state */
|
|
if (ppc->cap & PPCCAP_OEA)
|
|
{
|
|
/* PC to the reset vector; MSR has IP set to start */
|
|
ppc->pc = 0xfff00100;
|
|
ppc->msr = MSROEA_IP;
|
|
|
|
/* reset the decrementer */
|
|
ppc->dec_zero_cycles = cpu_get_total_cycles(Machine->cpu[ppc->cpunum]);
|
|
decrementer_int_callback(Machine, ppc, 0);
|
|
}
|
|
|
|
/* initialize the 4XX state */
|
|
if (ppc->cap & PPCCAP_4XX)
|
|
{
|
|
/* PC to the last word; MSR to 0 */
|
|
ppc->pc = 0xfffffffc;
|
|
ppc->msr = 0;
|
|
|
|
/* reset the SPU status */
|
|
ppc->spr[SPR4XX_TCR] &= ~PPC4XX_TCR_WRC_MASK;
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] = 0x06;
|
|
}
|
|
|
|
/* initialize the 602 HID0 register */
|
|
if (ppc->flavor == PPC_MODEL_602)
|
|
ppc->spr[SPR603_HID0] = 1;
|
|
|
|
/* time base starts here */
|
|
ppc->tb_zero_cycles = cpu_get_total_cycles(Machine->cpu[ppc->cpunum]);
|
|
|
|
/* clear interrupts */
|
|
ppc->irq_pending = 0;
|
|
|
|
/* flush the TLB */
|
|
vtlb_flush_dynamic(ppc->vtlb);
|
|
if (ppc->cap & PPCCAP_603_MMU)
|
|
for (tlbindex = 0; tlbindex < PPC603_FIXED_TLB_ENTRIES; tlbindex++)
|
|
vtlb_load(ppc->vtlb, tlbindex, 0, 0, 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_dasm - handle disassembly for a
|
|
CPU
|
|
-------------------------------------------------*/
|
|
|
|
offs_t ppccom_dasm(powerpc_state *ppc, char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram)
|
|
{
|
|
extern offs_t ppc_dasm_one(char *buffer, UINT32 pc, UINT32 op);
|
|
UINT32 op = *(UINT32 *)oprom;
|
|
op = BIG_ENDIANIZE_INT32(op);
|
|
return ppc_dasm_one(buffer, pc, op);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
TLB HANDLING
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_translate_address_internal - translate
|
|
an address from logical to physical; shared
|
|
between external requests and internal TLB
|
|
filling
|
|
-------------------------------------------------*/
|
|
|
|
static UINT32 ppccom_translate_address_internal(powerpc_state *ppc, int intention, offs_t *address)
|
|
{
|
|
int transpriv = ((intention & TRANSLATE_USER_MASK) == 0);
|
|
int transtype = intention & TRANSLATE_TYPE_MASK;
|
|
offs_t hash, hashbase, hashmask;
|
|
int batbase, batnum, hashnum;
|
|
UINT32 segreg;
|
|
|
|
/* 4xx case: "TLB" really just caches writes and checks compare registers */
|
|
if (ppc->cap & PPCCAP_4XX)
|
|
{
|
|
/* we don't support the MMU of the 403GCX */
|
|
if (ppc->flavor == PPC_MODEL_403GCX && (ppc->msr & MSROEA_DR))
|
|
fatalerror("MMU enabled but not supported!");
|
|
|
|
/* only check if PE is enabled */
|
|
if (transtype == TRANSLATE_WRITE && (ppc->msr & MSR4XX_PE))
|
|
{
|
|
/* are we within one of the protection ranges? */
|
|
int inrange1 = ((*address >> 12) >= (ppc->spr[SPR4XX_PBL1] >> 12) && (*address >> 12) < (ppc->spr[SPR4XX_PBU1] >> 12));
|
|
int inrange2 = ((*address >> 12) >= (ppc->spr[SPR4XX_PBL2] >> 12) && (*address >> 12) < (ppc->spr[SPR4XX_PBU2] >> 12));
|
|
|
|
/* if PX == 1, writes are only allowed OUTSIDE of the bounds */
|
|
if (((ppc->msr & MSR4XX_PX) && (inrange1 || inrange2)) || (!(ppc->msr & MSR4XX_PX) && (!inrange1 && !inrange2)))
|
|
return 0x002;
|
|
}
|
|
*address &= 0x7fffffff;
|
|
return 0x001;
|
|
}
|
|
|
|
/* only applies if we support the OEA */
|
|
if (!(ppc->cap & PPCCAP_OEA))
|
|
return 0x001;
|
|
|
|
/* also no translation necessary if translation is disabled */
|
|
if ((transtype == TRANSLATE_FETCH && (ppc->msr & MSROEA_IR) == 0) || (transtype != TRANSLATE_FETCH && (ppc->msr & MSROEA_DR) == 0))
|
|
return 0x001;
|
|
|
|
/* first scan the appropriate BAT */
|
|
batbase = (transtype == TRANSLATE_FETCH) ? SPROEA_IBAT0U : SPROEA_DBAT0U;
|
|
for (batnum = 0; batnum < 4; batnum++)
|
|
{
|
|
UINT32 upper = ppc->spr[batbase + 2*batnum + 0];
|
|
|
|
/* check user/supervisor valid bit */
|
|
if ((upper >> transpriv) & 0x01)
|
|
{
|
|
UINT32 mask = (~upper << 15) & 0xfffe0000;
|
|
|
|
/* check for a hit against this bucket */
|
|
if ((*address & mask) == (upper & mask))
|
|
{
|
|
UINT32 lower = ppc->spr[batbase + 2*batnum + 1];
|
|
|
|
/* verify protection; if we fail, return false and indicate a protection violation */
|
|
if (!page_access_allowed(transtype, 1, lower & 3))
|
|
return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
|
|
|
|
/* otherwise we're good */
|
|
*address = (lower & mask) | (*address & ~mask);
|
|
return 0x001;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* look up the segment register */
|
|
segreg = ppc->sr[*address >> 28];
|
|
if (transtype == TRANSLATE_FETCH && (segreg & 0x10000000))
|
|
return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
|
|
|
|
/* get hash table information from SD1 */
|
|
hashbase = ppc->spr[SPROEA_SDR1] & 0xffff0000;
|
|
hashmask = ((ppc->spr[SPROEA_SDR1] & 0x1ff) << 16) | 0xffff;
|
|
hash = (segreg & 0x7ffff) ^ ((*address >> 12) & 0xffff);
|
|
|
|
/* if we're simulating the 603 MMU, fill in the data and stop here */
|
|
if (ppc->cap & PPCCAP_603_MMU)
|
|
{
|
|
ppc->mmu603_cmp = 0x80000000 | ((segreg & 0xffffff) << 7) | (0 << 6) | ((*address >> 22) & 0x3f);
|
|
ppc->mmu603_hash[0] = hashbase | ((hash << 6) & hashmask);
|
|
ppc->mmu603_hash[1] = hashbase | ((~hash << 6) & hashmask);
|
|
return DSISR_NOT_FOUND | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
|
|
}
|
|
|
|
/* loop twice over hashes */
|
|
for (hashnum = 0; hashnum < 2; hashnum++)
|
|
{
|
|
offs_t ptegaddr = hashbase | ((hash << 6) & hashmask);
|
|
UINT32 *ptegptr = memory_get_read_ptr(cpunum_get_active(), ADDRESS_SPACE_PROGRAM, ptegaddr);
|
|
|
|
/* should only have valid memory here, but make sure */
|
|
if (ptegptr != NULL)
|
|
{
|
|
UINT32 targetupper = 0x80000000 | ((segreg & 0xffffff) << 7) | (hashnum << 6) | ((*address >> 22) & 0x3f);
|
|
int ptenum;
|
|
|
|
/* scan PTEs */
|
|
for (ptenum = 0; ptenum < 8; ptenum++)
|
|
if (ptegptr[BYTE_XOR_BE(ptenum * 2)] == targetupper)
|
|
{
|
|
UINT32 pteglower = ptegptr[BYTE_XOR_BE(ptenum * 2 + 1)];
|
|
|
|
/* verify protection; if we fail, return false and indicate a protection violation */
|
|
if (!page_access_allowed(transtype, (segreg >> (29 + transpriv)) & 1, pteglower & 3))
|
|
return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
|
|
|
|
/* update page table bits */
|
|
if (!(intention & TRANSLATE_DEBUG_MASK))
|
|
{
|
|
pteglower |= 0x100;
|
|
if (transtype == TRANSLATE_WRITE)
|
|
pteglower |= 0x080;
|
|
ptegptr[BYTE_XOR_BE(ptenum * 2 + 1)] = pteglower;
|
|
}
|
|
|
|
/* otherwise we're good */
|
|
*address = (pteglower & 0xfffff000) | (*address & 0x00000fff);
|
|
return (pteglower >> 7) & 1;
|
|
}
|
|
}
|
|
|
|
/* invert the hash after the first round */
|
|
hash = ~hash;
|
|
}
|
|
|
|
/* we failed to find any match: not found */
|
|
return DSISR_NOT_FOUND | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_translate_address - translate an address
|
|
from logical to physical
|
|
-------------------------------------------------*/
|
|
|
|
int ppccom_translate_address(powerpc_state *ppc, int space, int intention, offs_t *address)
|
|
{
|
|
/* only applies to the program address space */
|
|
if (space != ADDRESS_SPACE_PROGRAM)
|
|
return TRUE;
|
|
|
|
/* translation is successful if the internal routine returns 0 or 1 */
|
|
return (ppccom_translate_address_internal(ppc, intention, address) <= 1);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_tlb_fill - handle a missing TLB entry
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_tlb_fill(powerpc_state *ppc)
|
|
{
|
|
vtlb_fill(ppc->vtlb, ppc->param0, ppc->param1);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_tlb_flush - flush the entire TLB,
|
|
including fixed entries
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_tlb_flush(powerpc_state *ppc)
|
|
{
|
|
vtlb_flush_dynamic(ppc->vtlb);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
OPCODE HANDLING
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_tlbie - execute a TLBIE
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_tlbie(powerpc_state *ppc)
|
|
{
|
|
vtlb_flush_address(ppc->vtlb, ppc->param0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_tlbia - execute a TLBIA
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_tlbia(powerpc_state *ppc)
|
|
{
|
|
vtlb_flush_dynamic(ppc->vtlb);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_tlbl - execute a TLBLD/TLBLI
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_tlbl(powerpc_state *ppc)
|
|
{
|
|
UINT32 address = ppc->param0;
|
|
int isitlb = ppc->param1;
|
|
vtlb_entry flags = 0;
|
|
int entrynum;
|
|
|
|
/* determine entry number; we use rand() for associativity */
|
|
entrynum = ((address >> 12) & 0x1f) | (mame_rand(Machine) & 0x20) | (isitlb ? 0x40 : 0);
|
|
|
|
/* determine the flags */
|
|
flags = VTLB_FLAG_VALID | VTLB_READ_ALLOWED | VTLB_FETCH_ALLOWED;
|
|
if (ppc->spr[SPR603_RPA] & 0x80)
|
|
flags |= VTLB_WRITE_ALLOWED;
|
|
if (isitlb)
|
|
flags |= VTLB_FETCH_ALLOWED;
|
|
|
|
/* load the entry */
|
|
vtlb_load(ppc->vtlb, entrynum, 1, address, (ppc->spr[SPR603_RPA] & 0xfffff000) | flags);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_mftb - execute an MFTB
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_mftb(powerpc_state *ppc)
|
|
{
|
|
switch (ppc->param0)
|
|
{
|
|
/* user mode timebase read */
|
|
case SPRVEA_TBL_R:
|
|
ppc->param1 = get_timebase(ppc);
|
|
break;
|
|
case SPRVEA_TBU_R:
|
|
ppc->param1 = get_timebase(ppc) >> 32;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_mfspr - execute an MFSPR
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_mfspr(powerpc_state *ppc)
|
|
{
|
|
/* handle OEA SPRs */
|
|
if (ppc->cap & PPCCAP_OEA)
|
|
{
|
|
switch (ppc->param0)
|
|
{
|
|
/* read-through no-ops */
|
|
case SPROEA_DSISR:
|
|
case SPROEA_DAR:
|
|
case SPROEA_SDR1:
|
|
case SPROEA_SRR0:
|
|
case SPROEA_SRR1:
|
|
case SPROEA_EAR:
|
|
case SPROEA_IBAT0L:
|
|
case SPROEA_IBAT0U:
|
|
case SPROEA_IBAT1L:
|
|
case SPROEA_IBAT1U:
|
|
case SPROEA_IBAT2L:
|
|
case SPROEA_IBAT2U:
|
|
case SPROEA_IBAT3L:
|
|
case SPROEA_IBAT3U:
|
|
case SPROEA_DBAT0L:
|
|
case SPROEA_DBAT0U:
|
|
case SPROEA_DBAT1L:
|
|
case SPROEA_DBAT1U:
|
|
case SPROEA_DBAT2L:
|
|
case SPROEA_DBAT2U:
|
|
case SPROEA_DBAT3L:
|
|
case SPROEA_DBAT3U:
|
|
case SPROEA_DABR:
|
|
ppc->param1 = ppc->spr[ppc->param0];
|
|
return;
|
|
|
|
/* decrementer */
|
|
case SPROEA_DEC:
|
|
ppc->param1 = get_decrementer(ppc);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* handle 603 SPRs */
|
|
if (ppc->cap & PPCCAP_603_MMU)
|
|
{
|
|
switch (ppc->param0)
|
|
{
|
|
/* read-through no-ops */
|
|
case SPR603_DMISS:
|
|
case SPR603_DCMP:
|
|
case SPR603_HASH1:
|
|
case SPR603_HASH2:
|
|
case SPR603_IMISS:
|
|
case SPR603_ICMP:
|
|
case SPR603_RPA:
|
|
case SPR603_HID0:
|
|
case SPR603_HID1:
|
|
case SPR603_IABR:
|
|
case SPR603_HID2:
|
|
ppc->param1 = ppc->spr[ppc->param0];
|
|
return;
|
|
|
|
/* timebase */
|
|
case SPR603_TBL_R:
|
|
ppc->param1 = get_timebase(ppc);
|
|
return;
|
|
case SPR603_TBU_R:
|
|
ppc->param1 = (get_timebase(ppc) >> 32) & 0xffffff;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* handle 4XX SPRs */
|
|
if (ppc->cap & PPCCAP_4XX)
|
|
{
|
|
switch (ppc->param0)
|
|
{
|
|
/* read-through no-ops */
|
|
case SPR4XX_EVPR:
|
|
case SPR4XX_ESR:
|
|
case SPR4XX_SRR0:
|
|
case SPR4XX_SRR1:
|
|
case SPR4XX_SRR2:
|
|
case SPR4XX_SRR3:
|
|
case SPR4XX_TCR:
|
|
case SPR4XX_TSR:
|
|
case SPR4XX_IAC1:
|
|
case SPR4XX_IAC2:
|
|
case SPR4XX_DAC1:
|
|
case SPR4XX_DAC2:
|
|
case SPR4XX_DCCR:
|
|
case SPR4XX_ICCR:
|
|
case SPR4XX_PBL1:
|
|
case SPR4XX_PBU1:
|
|
case SPR4XX_PBL2:
|
|
case SPR4XX_PBU2:
|
|
ppc->param1 = ppc->spr[ppc->param0];
|
|
return;
|
|
|
|
/* timebase */
|
|
case SPR4XX_TBLO:
|
|
case SPR4XX_TBLU:
|
|
ppc->param1 = get_timebase(ppc);
|
|
return;
|
|
case SPR4XX_TBHI:
|
|
case SPR4XX_TBHU:
|
|
ppc->param1 = (get_timebase(ppc) >> 32) & 0xffffff;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* default handling */
|
|
mame_printf_debug("SPR %03X read\n", ppc->param0);
|
|
ppc->param1 = ppc->spr[ppc->param0];
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_mtspr - execute an MTSPR
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_mtspr(powerpc_state *ppc)
|
|
{
|
|
/* handle OEA SPRs */
|
|
if (ppc->cap & PPCCAP_OEA)
|
|
{
|
|
switch (ppc->param0)
|
|
{
|
|
/* write-through no-ops */
|
|
case SPROEA_DSISR:
|
|
case SPROEA_DAR:
|
|
case SPROEA_SRR0:
|
|
case SPROEA_SRR1:
|
|
case SPROEA_EAR:
|
|
case SPROEA_DABR:
|
|
ppc->spr[ppc->param0] = ppc->param1;
|
|
return;
|
|
|
|
/* registers that affect the memory map */
|
|
case SPROEA_SDR1:
|
|
case SPROEA_IBAT0L:
|
|
case SPROEA_IBAT0U:
|
|
case SPROEA_IBAT1L:
|
|
case SPROEA_IBAT1U:
|
|
case SPROEA_IBAT2L:
|
|
case SPROEA_IBAT2U:
|
|
case SPROEA_IBAT3L:
|
|
case SPROEA_IBAT3U:
|
|
case SPROEA_DBAT0L:
|
|
case SPROEA_DBAT0U:
|
|
case SPROEA_DBAT1L:
|
|
case SPROEA_DBAT1U:
|
|
case SPROEA_DBAT2L:
|
|
case SPROEA_DBAT2U:
|
|
case SPROEA_DBAT3L:
|
|
case SPROEA_DBAT3U:
|
|
ppc->spr[ppc->param0] = ppc->param1;
|
|
ppccom_tlb_flush(ppc);
|
|
return;
|
|
|
|
/* decrementer */
|
|
case SPROEA_DEC:
|
|
set_decrementer(ppc, ppc->param1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* handle 603 SPRs */
|
|
if (ppc->cap & PPCCAP_603_MMU)
|
|
{
|
|
switch (ppc->param0)
|
|
{
|
|
/* read-only */
|
|
case SPR603_DMISS:
|
|
case SPR603_DCMP:
|
|
case SPR603_HASH1:
|
|
case SPR603_HASH2:
|
|
case SPR603_IMISS:
|
|
case SPR603_ICMP:
|
|
return;
|
|
|
|
/* write-through no-ops */
|
|
case SPR603_RPA:
|
|
case SPR603_HID0:
|
|
case SPR603_HID1:
|
|
case SPR603_IABR:
|
|
case SPR603_HID2:
|
|
ppc->spr[ppc->param0] = ppc->param1;
|
|
return;
|
|
|
|
/* timebase */
|
|
case SPR603_TBL_W:
|
|
set_timebase(ppc, (get_timebase(ppc) & ~U64(0xffffffff00000000)) | ppc->param1);
|
|
return;
|
|
case SPR603_TBU_W:
|
|
set_timebase(ppc, (get_timebase(ppc) & ~U64(0x00000000ffffffff)) | ((UINT64)ppc->param1 << 32));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* handle 4XX SPRs */
|
|
if (ppc->cap & PPCCAP_4XX)
|
|
{
|
|
UINT32 oldval = ppc->spr[ppc->param0];
|
|
switch (ppc->param0)
|
|
{
|
|
/* write-through no-ops */
|
|
case SPR4XX_EVPR:
|
|
case SPR4XX_ESR:
|
|
case SPR4XX_DCCR:
|
|
case SPR4XX_ICCR:
|
|
case SPR4XX_SRR0:
|
|
case SPR4XX_SRR1:
|
|
case SPR4XX_SRR2:
|
|
case SPR4XX_SRR3:
|
|
ppc->spr[ppc->param0] = ppc->param1;
|
|
return;
|
|
|
|
/* registers that affect the memory map */
|
|
case SPR4XX_PBL1:
|
|
case SPR4XX_PBU1:
|
|
case SPR4XX_PBL2:
|
|
case SPR4XX_PBU2:
|
|
ppc->spr[ppc->param0] = ppc->param1;
|
|
ppccom_tlb_flush(ppc);
|
|
return;
|
|
|
|
/* timer control register */
|
|
case SPR4XX_TCR:
|
|
ppc->spr[SPR4XX_TCR] = ppc->param1 | (oldval & PPC4XX_TCR_WRC_MASK);
|
|
if ((oldval ^ ppc->spr[SPR4XX_TCR]) & PPC4XX_TCR_FIE)
|
|
ppc4xx_fit_callback(Machine, ppc, FALSE);
|
|
if ((oldval ^ ppc->spr[SPR4XX_TCR]) & PPC4XX_TCR_PIE)
|
|
ppc4xx_pit_callback(Machine, ppc, FALSE);
|
|
return;
|
|
|
|
/* timer status register */
|
|
case SPR4XX_TSR:
|
|
ppc->spr[SPR4XX_TSR] &= ~ppc->param1;
|
|
ppc4xx_set_irq_line(ppc, 0, 0);
|
|
return;
|
|
|
|
/* PIT */
|
|
case SPR4XX_PIT:
|
|
ppc->spr[SPR4XX_PIT] = ppc->param1;
|
|
ppc->pit_reload = ppc->param1;
|
|
ppc4xx_pit_callback(Machine, ppc, FALSE);
|
|
return;
|
|
|
|
/* timebase */
|
|
case SPR4XX_TBLO:
|
|
set_timebase(ppc, (get_timebase(ppc) & ~U64(0x00ffffff00000000)) | ppc->param1);
|
|
return;
|
|
case SPR4XX_TBHI:
|
|
set_timebase(ppc, (get_timebase(ppc) & ~U64(0x00000000ffffffff)) | ((UINT64)(ppc->param1 & 0x00ffffff) << 32));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* default handling */
|
|
mame_printf_debug("SPR %03X write = %08X\n", ppc->param0, ppc->param1);
|
|
ppc->spr[ppc->param0] = ppc->param1;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_mfdcr - execute an MFDCR
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_mfdcr(powerpc_state *ppc)
|
|
{
|
|
/* handle various DCRs */
|
|
switch (ppc->param0)
|
|
{
|
|
/* read-through no-ops */
|
|
case DCR4XX_BR0:
|
|
case DCR4XX_BR1:
|
|
case DCR4XX_BR2:
|
|
case DCR4XX_BR3:
|
|
case DCR4XX_BR4:
|
|
case DCR4XX_BR5:
|
|
case DCR4XX_BR6:
|
|
case DCR4XX_BR7:
|
|
case DCR4XX_BESR:
|
|
case DCR4XX_DMASR:
|
|
case DCR4XX_DMACT0:
|
|
case DCR4XX_DMADA0:
|
|
case DCR4XX_DMASA0:
|
|
case DCR4XX_DMACC0:
|
|
case DCR4XX_DMACR0:
|
|
case DCR4XX_DMACT1:
|
|
case DCR4XX_DMADA1:
|
|
case DCR4XX_DMASA1:
|
|
case DCR4XX_DMACC1:
|
|
case DCR4XX_DMACR1:
|
|
case DCR4XX_DMACT2:
|
|
case DCR4XX_DMADA2:
|
|
case DCR4XX_DMASA2:
|
|
case DCR4XX_DMACC2:
|
|
case DCR4XX_DMACR2:
|
|
case DCR4XX_DMACT3:
|
|
case DCR4XX_DMADA3:
|
|
case DCR4XX_DMASA3:
|
|
case DCR4XX_DMACC3:
|
|
case DCR4XX_DMACR3:
|
|
case DCR4XX_EXIER:
|
|
case DCR4XX_EXISR:
|
|
case DCR4XX_IOCR:
|
|
ppc->param1 = ppc->dcr[ppc->param0];
|
|
return;
|
|
}
|
|
|
|
/* default handling */
|
|
mame_printf_debug("DCR %03X read\n", ppc->param0);
|
|
if (ppc->param0 < ARRAY_LENGTH(ppc->dcr))
|
|
ppc->param1 = ppc->dcr[ppc->param0];
|
|
else
|
|
ppc->param1 = 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_execute_mtdcr - execute an MTDCR
|
|
instruction
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_execute_mtdcr(powerpc_state *ppc)
|
|
{
|
|
UINT8 oldval;
|
|
|
|
/* handle various DCRs */
|
|
switch (ppc->param0)
|
|
{
|
|
/* write-through no-ops */
|
|
case DCR4XX_BR0:
|
|
case DCR4XX_BR1:
|
|
case DCR4XX_BR2:
|
|
case DCR4XX_BR3:
|
|
case DCR4XX_BR4:
|
|
case DCR4XX_BR5:
|
|
case DCR4XX_BR6:
|
|
case DCR4XX_BR7:
|
|
case DCR4XX_BESR:
|
|
case DCR4XX_DMACT0:
|
|
case DCR4XX_DMADA0:
|
|
case DCR4XX_DMASA0:
|
|
case DCR4XX_DMACC0:
|
|
case DCR4XX_DMACT1:
|
|
case DCR4XX_DMADA1:
|
|
case DCR4XX_DMASA1:
|
|
case DCR4XX_DMACC1:
|
|
case DCR4XX_DMACT2:
|
|
case DCR4XX_DMADA2:
|
|
case DCR4XX_DMASA2:
|
|
case DCR4XX_DMACC2:
|
|
case DCR4XX_DMACT3:
|
|
case DCR4XX_DMADA3:
|
|
case DCR4XX_DMASA3:
|
|
case DCR4XX_DMACC3:
|
|
ppc->dcr[ppc->param0] = ppc->param1;
|
|
return;
|
|
|
|
/* DMA status */
|
|
case DCR4XX_DMASR:
|
|
ppc->dcr[DCR4XX_DMASR] &= ~(ppc->param1 & 0xfff80070);
|
|
ppc4xx_dma_update_irq_states(ppc);
|
|
return;
|
|
|
|
/* interrupt enables */
|
|
case DCR4XX_EXIER:
|
|
ppc->dcr[DCR4XX_EXIER] = ppc->param1;
|
|
ppc4xx_set_irq_line(ppc, 0, 0);
|
|
return;
|
|
|
|
/* interrupt clear */
|
|
case DCR4XX_EXISR:
|
|
ppc->dcr[ppc->param0] &= ~ppc->param1;
|
|
ppc4xx_set_irq_line(ppc, 0, 0);
|
|
return;
|
|
|
|
/* DMA controls */
|
|
case DCR4XX_DMACR0:
|
|
case DCR4XX_DMACR1:
|
|
case DCR4XX_DMACR2:
|
|
case DCR4XX_DMACR3:
|
|
ppc->dcr[ppc->param0] = ppc->param1;
|
|
if (ppc->param1 & PPC4XX_DMACR_CE)
|
|
ppc4xx_dma_exec(ppc, (ppc->param0 - DCR4XX_DMACR0) / 8);
|
|
ppc4xx_dma_update_irq_states(ppc);
|
|
return;
|
|
|
|
/* I/O control */
|
|
case DCR4XX_IOCR:
|
|
oldval = ppc->dcr[ppc->param0];
|
|
ppc->dcr[ppc->param0] = ppc->param1;
|
|
if ((oldval ^ ppc->param1) & 0x02)
|
|
ppc4xx_spu_timer_reset(ppc);
|
|
return;
|
|
}
|
|
|
|
/* default handling */
|
|
mame_printf_debug("DCR %03X write = %08X\n", ppc->param0, ppc->param1);
|
|
if (ppc->param0 < ARRAY_LENGTH(ppc->dcr))
|
|
ppc->dcr[ppc->param0] = ppc->param1;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
COMMON GET/SET INFO
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_set_info - set information about
|
|
a PowerPC CPU
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_set_info(powerpc_state *ppc, UINT32 state, cpuinfo *info)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are set as 64-bit signed integers --- */
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ: ppc->irq_pending = (ppc->irq_pending & ~1) | (info->i != CLEAR_LINE); break;
|
|
|
|
case CPUINFO_INT_PC:
|
|
case CPUINFO_INT_REGISTER + PPC_PC: ppc->pc = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_MSR: ppc->msr = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_CR: set_cr(ppc, info->i); break;
|
|
case CPUINFO_INT_REGISTER + PPC_LR: ppc->spr[SPR_LR] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_CTR: ppc->spr[SPR_CTR] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_XER: set_xer(ppc, info->i); break;
|
|
case CPUINFO_INT_REGISTER + PPC_SRR0: ppc->spr[SPROEA_SRR0] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SRR1: ppc->spr[SPROEA_SRR1] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG0: ppc->spr[SPROEA_SPRG0] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG1: ppc->spr[SPROEA_SPRG1] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG2: ppc->spr[SPROEA_SPRG2] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG3: ppc->spr[SPROEA_SPRG3] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SDR1: ppc->spr[SPROEA_SDR1] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_EXIER: ppc->dcr[DCR4XX_EXIER] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_EXISR: ppc->dcr[DCR4XX_EXISR] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_EVPR: ppc->spr[SPR4XX_EVPR] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_IOCR: ppc->dcr[DCR4XX_IOCR] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_TBL: set_timebase(ppc, (get_timebase(ppc) & ~U64(0x00ffffff00000000)) | info->i); break;
|
|
case CPUINFO_INT_REGISTER + PPC_TBH: set_timebase(ppc, (get_timebase(ppc) & ~U64(0x00000000ffffffff)) | ((UINT64)(ppc->param1 & 0x00ffffff) << 32)); break;
|
|
case CPUINFO_INT_REGISTER + PPC_DEC: set_decrementer(ppc, info->i); break;
|
|
|
|
case CPUINFO_INT_REGISTER + PPC_R0: ppc->r[0] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R1: ppc->r[1] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R2: ppc->r[2] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R3: ppc->r[3] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R4: ppc->r[4] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R5: ppc->r[5] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R6: ppc->r[6] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R7: ppc->r[7] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R8: ppc->r[8] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R9: ppc->r[9] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R10: ppc->r[10] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R11: ppc->r[11] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R12: ppc->r[12] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R13: ppc->r[13] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R14: ppc->r[14] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R15: ppc->r[15] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R16: ppc->r[16] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R17: ppc->r[17] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R18: ppc->r[18] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R19: ppc->r[19] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R20: ppc->r[20] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R21: ppc->r[21] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R22: ppc->r[22] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R23: ppc->r[23] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R24: ppc->r[24] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R25: ppc->r[25] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R26: ppc->r[26] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R27: ppc->r[27] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R28: ppc->r[28] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R29: ppc->r[29] = info->i; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R30: ppc->r[30] = info->i; break;
|
|
case CPUINFO_INT_SP:
|
|
case CPUINFO_INT_REGISTER + PPC_R31: ppc->r[31] = info->i; break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppccom_get_info - get information about
|
|
a PowerPC CPU
|
|
-------------------------------------------------*/
|
|
|
|
void ppccom_get_info(powerpc_state *ppc, UINT32 state, cpuinfo *info)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
|
case CPUINFO_INT_CONTEXT_SIZE: /* provided by core */ break;
|
|
case CPUINFO_INT_INPUT_LINES: info->i = 1; break;
|
|
case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0; break;
|
|
case CPUINFO_INT_ENDIANNESS: info->i = CPU_IS_BE; break;
|
|
case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break;
|
|
case CPUINFO_INT_CLOCK_DIVIDER: info->i = 1; break;
|
|
case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 4; break;
|
|
case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 4; break;
|
|
case CPUINFO_INT_MIN_CYCLES: info->i = 1; break;
|
|
case CPUINFO_INT_MAX_CYCLES: info->i = 40; break;
|
|
|
|
case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 64; break;
|
|
case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32; break;
|
|
case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = 0; break;
|
|
case CPUINFO_INT_LOGADDR_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32; break;
|
|
case CPUINFO_INT_PAGE_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = POWERPC_MIN_PAGE_SHIFT;break;
|
|
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ: info->i = ppc->irq_pending ? ASSERT_LINE : CLEAR_LINE; break;
|
|
|
|
case CPUINFO_INT_PREVIOUSPC: /* optionally implemented */ break;
|
|
|
|
case CPUINFO_INT_PC:
|
|
case CPUINFO_INT_REGISTER + PPC_PC: info->i = ppc->pc; break;
|
|
case CPUINFO_INT_REGISTER + PPC_MSR: info->i = ppc->msr; break;
|
|
case CPUINFO_INT_REGISTER + PPC_CR: info->i = get_cr(ppc); break;
|
|
case CPUINFO_INT_REGISTER + PPC_LR: info->i = ppc->spr[SPR_LR]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_CTR: info->i = ppc->spr[SPR_CTR]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_XER: info->i = get_xer(ppc); break;
|
|
case CPUINFO_INT_REGISTER + PPC_SRR0: info->i = ppc->spr[SPROEA_SRR0]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SRR1: info->i = ppc->spr[SPROEA_SRR1]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG0: info->i = ppc->spr[SPROEA_SPRG0]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG1: info->i = ppc->spr[SPROEA_SPRG1]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG2: info->i = ppc->spr[SPROEA_SPRG2]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SPRG3: info->i = ppc->spr[SPROEA_SPRG3]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_SDR1: info->i = ppc->spr[SPROEA_SDR1]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_EXIER: info->i = ppc->dcr[DCR4XX_EXIER]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_EXISR: info->i = ppc->dcr[DCR4XX_EXISR]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_EVPR: info->i = ppc->spr[SPR4XX_EVPR]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_IOCR: info->i = ppc->dcr[DCR4XX_IOCR]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_TBH: info->i = get_timebase(ppc) >> 32; break;
|
|
case CPUINFO_INT_REGISTER + PPC_TBL: info->i = (UINT32)get_timebase(ppc); break;
|
|
case CPUINFO_INT_REGISTER + PPC_DEC: info->i = get_decrementer(ppc); break;
|
|
|
|
case CPUINFO_INT_REGISTER + PPC_R0: info->i = ppc->r[0]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R1: info->i = ppc->r[1]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R2: info->i = ppc->r[2]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R3: info->i = ppc->r[3]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R4: info->i = ppc->r[4]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R5: info->i = ppc->r[5]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R6: info->i = ppc->r[6]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R7: info->i = ppc->r[7]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R8: info->i = ppc->r[8]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R9: info->i = ppc->r[9]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R10: info->i = ppc->r[10]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R11: info->i = ppc->r[11]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R12: info->i = ppc->r[12]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R13: info->i = ppc->r[13]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R14: info->i = ppc->r[14]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R15: info->i = ppc->r[15]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R16: info->i = ppc->r[16]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R17: info->i = ppc->r[17]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R18: info->i = ppc->r[18]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R19: info->i = ppc->r[19]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R20: info->i = ppc->r[20]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R21: info->i = ppc->r[21]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R22: info->i = ppc->r[22]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R23: info->i = ppc->r[23]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R24: info->i = ppc->r[24]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R25: info->i = ppc->r[25]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R26: info->i = ppc->r[26]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R27: info->i = ppc->r[27]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R28: info->i = ppc->r[28]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R29: info->i = ppc->r[29]; break;
|
|
case CPUINFO_INT_REGISTER + PPC_R30: info->i = ppc->r[30]; break;
|
|
case CPUINFO_INT_SP:
|
|
case CPUINFO_INT_REGISTER + PPC_R31: info->i = ppc->r[31]; break;
|
|
|
|
/* --- the following bits of info are returned as pointers to data or functions --- */
|
|
case CPUINFO_PTR_SET_INFO: /* provided by core */ break;
|
|
case CPUINFO_PTR_GET_CONTEXT: /* provided by core */ break;
|
|
case CPUINFO_PTR_SET_CONTEXT: /* provided by core */ break;
|
|
case CPUINFO_PTR_INIT: /* provided by core */ break;
|
|
case CPUINFO_PTR_RESET: /* provided by core */ break;
|
|
case CPUINFO_PTR_EXIT: /* provided by core */ break;
|
|
case CPUINFO_PTR_EXECUTE: /* provided by core */ break;
|
|
case CPUINFO_PTR_TRANSLATE: /* provided by core */ break;
|
|
case CPUINFO_PTR_DISASSEMBLE: /* provided by core */ break;
|
|
case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &ppc->icount; break;
|
|
|
|
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
|
case CPUINFO_STR_NAME: strcpy(info->s, "PowerPC"); break;
|
|
case CPUINFO_STR_CORE_FAMILY: strcpy(info->s, "PowerPC"); break;
|
|
case CPUINFO_STR_CORE_VERSION: strcpy(info->s, "2.0"); break;
|
|
case CPUINFO_STR_CORE_FILE: /* provided by core */ break;
|
|
case CPUINFO_STR_CORE_CREDITS: strcpy(info->s, "Copyright Aaron Giles"); break;
|
|
|
|
case CPUINFO_STR_FLAGS: strcpy(info->s, " "); break;
|
|
|
|
case CPUINFO_STR_REGISTER + PPC_PC: sprintf(info->s, "PC: %08X", ppc->pc); break;
|
|
case CPUINFO_STR_REGISTER + PPC_MSR: sprintf(info->s, "MSR:%08X", ppc->msr); break;
|
|
case CPUINFO_STR_REGISTER + PPC_CR: sprintf(info->s, "CR: %08X", get_cr(ppc)); break;
|
|
case CPUINFO_STR_REGISTER + PPC_LR: sprintf(info->s, "LR: %08X", ppc->spr[SPR_LR]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_CTR: sprintf(info->s, "CTR:%08X", ppc->spr[SPR_CTR]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_XER: sprintf(info->s, "XER:%08X", get_xer(ppc)); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SRR0: sprintf(info->s, "SRR0: %08X", ppc->spr[SPROEA_SRR0]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SRR1: sprintf(info->s, "SRR1: %08X", ppc->spr[SPROEA_SRR1]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SPRG0: sprintf(info->s, "SPRG0: %08X", ppc->spr[SPROEA_SPRG0]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SPRG1: sprintf(info->s, "SPRG1: %08X", ppc->spr[SPROEA_SPRG1]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SPRG2: sprintf(info->s, "SPRG2: %08X", ppc->spr[SPROEA_SPRG2]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SPRG3: sprintf(info->s, "SPRG3: %08X", ppc->spr[SPROEA_SPRG3]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_SDR1: sprintf(info->s, "SDR1: %08X", ppc->spr[SPROEA_SDR1]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_EXIER: sprintf(info->s, "EXIER: %08X", ppc->dcr[DCR4XX_EXIER]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_EXISR: sprintf(info->s, "EXISR: %08X", ppc->dcr[DCR4XX_EXISR]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_EVPR: sprintf(info->s, "EVPR: %08X", ppc->spr[SPR4XX_EVPR]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_IOCR: sprintf(info->s, "IOCR: %08X", ppc->dcr[DCR4XX_EXISR]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_TBH: sprintf(info->s, "TBH: %08X", (UINT32)(get_timebase(ppc) >> 32)); break;
|
|
case CPUINFO_STR_REGISTER + PPC_TBL: sprintf(info->s, "TBL: %08X", (UINT32)get_timebase(ppc)); break;
|
|
case CPUINFO_STR_REGISTER + PPC_DEC: sprintf(info->s, "DEC: %08X", get_decrementer(ppc)); break;
|
|
|
|
case CPUINFO_STR_REGISTER + PPC_R0: sprintf(info->s, "R0: %08X", ppc->r[0]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R1: sprintf(info->s, "R1: %08X", ppc->r[1]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R2: sprintf(info->s, "R2: %08X", ppc->r[2]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R3: sprintf(info->s, "R3: %08X", ppc->r[3]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R4: sprintf(info->s, "R4: %08X", ppc->r[4]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R5: sprintf(info->s, "R5: %08X", ppc->r[5]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R6: sprintf(info->s, "R6: %08X", ppc->r[6]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R7: sprintf(info->s, "R7: %08X", ppc->r[7]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R8: sprintf(info->s, "R8: %08X", ppc->r[8]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R9: sprintf(info->s, "R9: %08X", ppc->r[9]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R10: sprintf(info->s, "R10:%08X", ppc->r[10]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R11: sprintf(info->s, "R11:%08X", ppc->r[11]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R12: sprintf(info->s, "R12:%08X", ppc->r[12]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R13: sprintf(info->s, "R13:%08X", ppc->r[13]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R14: sprintf(info->s, "R14:%08X", ppc->r[14]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R15: sprintf(info->s, "R15:%08X", ppc->r[15]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R16: sprintf(info->s, "R16:%08X", ppc->r[16]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R17: sprintf(info->s, "R17:%08X", ppc->r[17]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R18: sprintf(info->s, "R18:%08X", ppc->r[18]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R19: sprintf(info->s, "R19:%08X", ppc->r[19]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R20: sprintf(info->s, "R20:%08X", ppc->r[20]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R21: sprintf(info->s, "R21:%08X", ppc->r[21]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R22: sprintf(info->s, "R22:%08X", ppc->r[22]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R23: sprintf(info->s, "R23:%08X", ppc->r[23]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R24: sprintf(info->s, "R24:%08X", ppc->r[24]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R25: sprintf(info->s, "R25:%08X", ppc->r[25]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R26: sprintf(info->s, "R26:%08X", ppc->r[26]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R27: sprintf(info->s, "R27:%08X", ppc->r[27]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R28: sprintf(info->s, "R28:%08X", ppc->r[28]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R29: sprintf(info->s, "R29:%08X", ppc->r[29]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R30: sprintf(info->s, "R30:%08X", ppc->r[30]); break;
|
|
case CPUINFO_STR_REGISTER + PPC_R31: sprintf(info->s, "R31:%08X", ppc->r[31]); break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
OEA HELPERS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
decrementer_int_callback - callback that fires
|
|
whenever a decrementer interrupt is generated
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( decrementer_int_callback )
|
|
{
|
|
powerpc_state *ppc = ptr;
|
|
UINT64 cycles_until_next;
|
|
|
|
/* set the decrementer IRQ state */
|
|
ppc->irq_pending |= 0x02;
|
|
|
|
/* advance by another full rev */
|
|
ppc->dec_zero_cycles += (UINT64)ppc->tb_divisor << 32;
|
|
cycles_until_next = ppc->dec_zero_cycles - cpu_get_total_cycles(machine->cpu[ppc->cpunum]);
|
|
timer_adjust_oneshot(ppc->decrementer_int_timer, ATTOTIME_IN_CYCLES(cycles_until_next, ppc->cpunum), 0);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
EMBEDDED 4XX HELPERS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_set_irq_line - PowerPC 4XX-specific
|
|
IRQ line management
|
|
-------------------------------------------------*/
|
|
|
|
static void ppc4xx_set_irq_line(powerpc_state *ppc, UINT32 bitmask, int state)
|
|
{
|
|
UINT32 oldstate = ppc->irqstate;
|
|
UINT32 levelmask;
|
|
|
|
/* set or clear the appropriate bit */
|
|
if (state != CLEAR_LINE)
|
|
ppc->irqstate |= bitmask;
|
|
else
|
|
ppc->irqstate &= ~bitmask;
|
|
|
|
/* if the state changed to on, edge trigger the interrupt */
|
|
if (((ppc->irqstate ^ oldstate) & bitmask) && (ppc->irqstate & bitmask))
|
|
ppc->dcr[DCR4XX_EXISR] |= bitmask;
|
|
|
|
/* pass through all level-triggered interrupts */
|
|
levelmask = PPC4XX_IRQ_BIT_CRITICAL | PPC4XX_IRQ_BIT_SPUR | PPC4XX_IRQ_BIT_SPUT;
|
|
levelmask |= PPC4XX_IRQ_BIT_JTAGR | PPC4XX_IRQ_BIT_JTAGT;
|
|
levelmask |= PPC4XX_IRQ_BIT_DMA0 | PPC4XX_IRQ_BIT_DMA1 | PPC4XX_IRQ_BIT_DMA2 | PPC4XX_IRQ_BIT_DMA3;
|
|
if (!(ppc->dcr[DCR4XX_IOCR] & 0x80000000)) levelmask |= PPC4XX_IRQ_BIT_EXT0;
|
|
if (!(ppc->dcr[DCR4XX_IOCR] & 0x20000000)) levelmask |= PPC4XX_IRQ_BIT_EXT1;
|
|
if (!(ppc->dcr[DCR4XX_IOCR] & 0x08000000)) levelmask |= PPC4XX_IRQ_BIT_EXT2;
|
|
if (!(ppc->dcr[DCR4XX_IOCR] & 0x02000000)) levelmask |= PPC4XX_IRQ_BIT_EXT3;
|
|
if (!(ppc->dcr[DCR4XX_IOCR] & 0x00800000)) levelmask |= PPC4XX_IRQ_BIT_EXT4;
|
|
ppc->dcr[DCR4XX_EXISR] = (ppc->dcr[DCR4XX_EXISR] & ~levelmask) | (ppc->irqstate & levelmask);
|
|
|
|
/* update the IRQ status */
|
|
ppc->irq_pending = ((ppc->dcr[DCR4XX_EXISR] & ppc->dcr[DCR4XX_EXIER]) != 0);
|
|
if ((ppc->spr[SPR4XX_TCR] & PPC4XX_TCR_FIE) && (ppc->spr[SPR4XX_TSR] & PPC4XX_TSR_FIS))
|
|
ppc->irq_pending = TRUE;
|
|
if ((ppc->spr[SPR4XX_TCR] & PPC4XX_TCR_PIE) && (ppc->spr[SPR4XX_TSR] & PPC4XX_TSR_PIS))
|
|
ppc->irq_pending = TRUE;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_get_irq_line - PowerPC 4XX-specific
|
|
IRQ line state getter
|
|
-------------------------------------------------*/
|
|
|
|
static int ppc4xx_get_irq_line(powerpc_state *ppc, UINT32 bitmask)
|
|
{
|
|
return (ppc->irqstate & bitmask) ? ASSERT_LINE : CLEAR_LINE;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_dma_update_irq_states - update the IRQ
|
|
state for each DMA channel
|
|
-------------------------------------------------*/
|
|
|
|
static void ppc4xx_dma_update_irq_states(powerpc_state *ppc)
|
|
{
|
|
int dmachan;
|
|
|
|
/* update the IRQ state for each DMA channel */
|
|
for (dmachan = 0; dmachan < 4; dmachan++)
|
|
if ((ppc->dcr[DCR4XX_DMACR0 + 8 * dmachan] & PPC4XX_DMACR_CIE) && (ppc->dcr[DCR4XX_DMASR] & (0x11 << (27 - dmachan))))
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_DMA(dmachan), ASSERT_LINE);
|
|
else
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_DMA(dmachan), CLEAR_LINE);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_dma_decrement_count - decrement the
|
|
count on a channel and interrupt if configured
|
|
to do so
|
|
-------------------------------------------------*/
|
|
|
|
static int ppc4xx_dma_decrement_count(powerpc_state *ppc, int dmachan)
|
|
{
|
|
UINT32 *dmaregs = &ppc->dcr[8 * dmachan];
|
|
|
|
/* decrement the counter */
|
|
dmaregs[DCR4XX_DMACT0]--;
|
|
|
|
/* if non-zero, we keep going */
|
|
if ((dmaregs[DCR4XX_DMACT0] & 0xffff) != 0)
|
|
return FALSE;
|
|
|
|
/* set the complete bit and handle interrupts */
|
|
ppc->dcr[DCR4XX_DMASR] |= 1 << (31 - dmachan);
|
|
// ppc->dcr[DCR4XX_DMASR] |= 1 << (27 - dmachan);
|
|
ppc4xx_dma_update_irq_states(ppc);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_dma_fetch_transmit_byte - fetch a byte
|
|
to send to a peripheral
|
|
-------------------------------------------------*/
|
|
|
|
static int ppc4xx_dma_fetch_transmit_byte(powerpc_state *ppc, int dmachan, UINT8 *byte)
|
|
{
|
|
UINT32 *dmaregs = &ppc->dcr[8 * dmachan];
|
|
|
|
/* if the channel is not enabled, fail */
|
|
if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CE))
|
|
return FALSE;
|
|
|
|
/* if no transfers remaining, fail */
|
|
if ((dmaregs[DCR4XX_DMACT0] & 0xffff) == 0)
|
|
return FALSE;
|
|
|
|
/* fetch the data */
|
|
cpu_push_context(Machine->cpu[ppc->cpunum]);
|
|
*byte = program_read_byte(dmaregs[DCR4XX_DMADA0]++);
|
|
ppc4xx_dma_decrement_count(ppc, dmachan);
|
|
cpu_pop_context();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_dma_handle_receive_byte - receive a byte
|
|
transmitted by a peripheral
|
|
-------------------------------------------------*/
|
|
|
|
static int ppc4xx_dma_handle_receive_byte(powerpc_state *ppc, int dmachan, UINT8 byte)
|
|
{
|
|
UINT32 *dmaregs = &ppc->dcr[8 * dmachan];
|
|
|
|
/* if the channel is not enabled, fail */
|
|
if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CE))
|
|
return FALSE;
|
|
|
|
/* if no transfers remaining, fail */
|
|
if ((dmaregs[DCR4XX_DMACT0] & 0xffff) == 0)
|
|
return FALSE;
|
|
|
|
/* store the data */
|
|
cpu_push_context(Machine->cpu[ppc->cpunum]);
|
|
program_write_byte(dmaregs[DCR4XX_DMADA0]++, byte);
|
|
ppc4xx_dma_decrement_count(ppc, dmachan);
|
|
cpu_pop_context();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_dma_execute - execute a DMA operation
|
|
if one is pending
|
|
-------------------------------------------------*/
|
|
|
|
static void ppc4xx_dma_exec(powerpc_state *ppc, int dmachan)
|
|
{
|
|
static const UINT8 dma_transfer_width[4] = { 1, 2, 4, 16 };
|
|
UINT32 *dmaregs = &ppc->dcr[8 * dmachan];
|
|
INT32 destinc, srcinc;
|
|
UINT8 width;
|
|
|
|
/* skip if not enabled */
|
|
if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CE))
|
|
return;
|
|
|
|
/* check for unsupported features */
|
|
if (!(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_TCE))
|
|
fatalerror("ppc4xx_dma_exec: DMA_TCE == 0");
|
|
if (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_CH)
|
|
fatalerror("ppc4xx_dma_exec: DMA chaining not implemented");
|
|
|
|
/* transfer mode */
|
|
switch ((dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_TM_MASK) >> 21)
|
|
{
|
|
/* buffered mode DMA */
|
|
case 0:
|
|
/* nothing to do; this happens asynchronously and is driven by the SPU */
|
|
break;
|
|
|
|
/* fly-by mode DMA */
|
|
case 1:
|
|
fatalerror("ppc4xx_dma_exec: fly-by DMA not implemented");
|
|
break;
|
|
|
|
/* software initiated memory-to-memory mode DMA */
|
|
case 2:
|
|
width = dma_transfer_width[(dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_PW_MASK) >> 26];
|
|
srcinc = (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_SAI) ? width : 0;
|
|
destinc = (dmaregs[DCR4XX_DMACR0] & PPC4XX_DMACR_DAI) ? width : 0;
|
|
|
|
switch (width)
|
|
{
|
|
/* byte transfer */
|
|
case 1:
|
|
do
|
|
{
|
|
program_write_byte(dmaregs[DCR4XX_DMADA0], program_read_byte(dmaregs[DCR4XX_DMASA0]));
|
|
dmaregs[DCR4XX_DMASA0] += srcinc;
|
|
dmaregs[DCR4XX_DMADA0] += destinc;
|
|
} while (!ppc4xx_dma_decrement_count(ppc, dmachan));
|
|
break;
|
|
|
|
/* word transfer */
|
|
case 2:
|
|
do
|
|
{
|
|
program_write_word(dmaregs[DCR4XX_DMADA0], program_read_word(dmaregs[DCR4XX_DMASA0]));
|
|
dmaregs[DCR4XX_DMASA0] += srcinc;
|
|
dmaregs[DCR4XX_DMADA0] += destinc;
|
|
} while (!ppc4xx_dma_decrement_count(ppc, dmachan));
|
|
break;
|
|
|
|
/* dword transfer */
|
|
case 4:
|
|
do
|
|
{
|
|
program_write_dword(dmaregs[DCR4XX_DMADA0], program_read_dword(dmaregs[DCR4XX_DMASA0]));
|
|
dmaregs[DCR4XX_DMASA0] += srcinc;
|
|
dmaregs[DCR4XX_DMADA0] += destinc;
|
|
} while (!ppc4xx_dma_decrement_count(ppc, dmachan));
|
|
break;
|
|
|
|
/* 16-byte transfer */
|
|
case 16:
|
|
do
|
|
{
|
|
program_write_qword(dmaregs[DCR4XX_DMADA0], program_read_qword(dmaregs[DCR4XX_DMASA0]));
|
|
program_write_qword(dmaregs[DCR4XX_DMADA0] + 8, program_read_qword(dmaregs[DCR4XX_DMASA0] + 8));
|
|
dmaregs[DCR4XX_DMASA0] += srcinc;
|
|
dmaregs[DCR4XX_DMADA0] += destinc;
|
|
} while (!ppc4xx_dma_decrement_count(ppc, dmachan));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* hardware initiated memory-to-memory mode DMA */
|
|
case 3:
|
|
fatalerror("ppc4xx_dma_exec: HW mem-to-mem DMA not implemented");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_fit_callback - FIT timer callback
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( ppc4xx_fit_callback )
|
|
{
|
|
powerpc_state *ppc = ptr;
|
|
|
|
/* if this is a real callback and we are enabled, signal an interrupt */
|
|
if (param)
|
|
{
|
|
ppc->spr[SPR4XX_TSR] |= PPC4XX_TSR_FIS;
|
|
ppc4xx_set_irq_line(ppc, 0, 0);
|
|
}
|
|
|
|
/* update ourself for the next interval if we are enabled */
|
|
if (ppc->spr[SPR4XX_TCR] & PPC4XX_TCR_FIE)
|
|
{
|
|
UINT32 timebase = get_timebase(ppc);
|
|
UINT32 interval = 0x200 << (4 * ((ppc->spr[SPR4XX_TCR] & PPC4XX_TCR_FP_MASK) >> 24));
|
|
UINT32 target = (timebase + interval) & ~(interval - 1);
|
|
timer_adjust_oneshot(ppc->fit_timer, ATTOTIME_IN_CYCLES((target + 1 - timebase) / ppc->tb_divisor, ppc->cpunum), TRUE);
|
|
}
|
|
|
|
/* otherwise, turn ourself off */
|
|
else
|
|
timer_adjust_oneshot(ppc->fit_timer, attotime_never, FALSE);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_pit_callback - PIT timer callback
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( ppc4xx_pit_callback )
|
|
{
|
|
powerpc_state *ppc = ptr;
|
|
|
|
/* if this is a real callback and we are enabled, signal an interrupt */
|
|
if (param)
|
|
{
|
|
ppc->spr[SPR4XX_TSR] |= PPC4XX_TSR_PIS;
|
|
ppc4xx_set_irq_line(ppc, 0, 0);
|
|
}
|
|
|
|
/* update ourself for the next interval if we are enabled and we are either being
|
|
forced to update, or we are in auto-reload mode */
|
|
if ((ppc->spr[SPR4XX_TCR] & PPC4XX_TCR_PIE) && ppc->pit_reload != 0 && (!param || (ppc->spr[SPR4XX_TCR] & PPC4XX_TCR_ARE)))
|
|
{
|
|
UINT32 timebase = get_timebase(ppc);
|
|
UINT32 interval = ppc->pit_reload;
|
|
UINT32 target = timebase + interval;
|
|
timer_adjust_oneshot(ppc->pit_timer, ATTOTIME_IN_CYCLES((target + 1 - timebase) / ppc->tb_divisor, ppc->cpunum), TRUE);
|
|
}
|
|
|
|
/* otherwise, turn ourself off */
|
|
else
|
|
timer_adjust_oneshot(ppc->pit_timer, attotime_never, FALSE);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_spu_update_irq_states - update the IRQ
|
|
state for the SPU
|
|
-------------------------------------------------*/
|
|
|
|
static void ppc4xx_spu_update_irq_states(powerpc_state *ppc)
|
|
{
|
|
/* check for receive buffer full interrupt */
|
|
if ((ppc->spu.regs[SPU4XX_RX_COMMAND] & 0x60) == 0x20 && (ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x80))
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_SPUR, ASSERT_LINE);
|
|
|
|
/* check for receive error interrupt */
|
|
else if ((ppc->spu.regs[SPU4XX_RX_COMMAND] & 0x10) && (ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x78))
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_SPUR, ASSERT_LINE);
|
|
|
|
/* clear otherwise */
|
|
else
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_SPUR, CLEAR_LINE);
|
|
|
|
/* check for transmit buffer empty interrupt */
|
|
if ((ppc->spu.regs[SPU4XX_TX_COMMAND] & 0x60) == 0x20 && (ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x04))
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_SPUT, ASSERT_LINE);
|
|
|
|
/* check for shift register empty interrupt */
|
|
else if ((ppc->spu.regs[SPU4XX_TX_COMMAND] & 0x10) && (ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x02))
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_SPUT, ASSERT_LINE);
|
|
|
|
/* clear otherwise */
|
|
else
|
|
ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_SPUT, CLEAR_LINE);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_spu_rx_data - serial port data receive
|
|
-------------------------------------------------*/
|
|
|
|
static void ppc4xx_spu_rx_data(powerpc_state *ppc, UINT8 data)
|
|
{
|
|
UINT32 new_rxin;
|
|
|
|
/* fail if we are going to overflow */
|
|
new_rxin = (ppc->spu.rxin + 1) % ARRAY_LENGTH(ppc->spu.rxbuffer);
|
|
if (new_rxin == ppc->spu.rxout)
|
|
fatalerror("ppc4xx_spu_rx_data: buffer overrun!");
|
|
|
|
/* store the data and accept the new in index */
|
|
ppc->spu.rxbuffer[ppc->spu.rxin] = data;
|
|
ppc->spu.rxin = new_rxin;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_spu_timer_reset - reset and recompute
|
|
the transmit/receive timer
|
|
-------------------------------------------------*/
|
|
|
|
static void ppc4xx_spu_timer_reset(powerpc_state *ppc)
|
|
{
|
|
UINT8 enabled = (ppc->spu.regs[SPU4XX_RX_COMMAND] | ppc->spu.regs[SPU4XX_TX_COMMAND]) & 0x80;
|
|
|
|
/* if we're enabled, reset at the current baud rate */
|
|
if (enabled)
|
|
{
|
|
attotime clockperiod = ATTOTIME_IN_HZ((ppc->dcr[DCR4XX_IOCR] & 0x02) ? 3686400 : 33333333);
|
|
int divisor = ((ppc->spu.regs[SPU4XX_BAUD_DIVISOR_H] * 256 + ppc->spu.regs[SPU4XX_BAUD_DIVISOR_L]) & 0xfff) + 1;
|
|
int bpc = 7 + ((ppc->spu.regs[SPU4XX_CONTROL] & 8) >> 3) + 1 + (ppc->spu.regs[SPU4XX_CONTROL] & 1);
|
|
attotime charperiod = attotime_mul(clockperiod, divisor * 16 * bpc);
|
|
timer_adjust_periodic(ppc->spu.timer, charperiod, 0, charperiod);
|
|
if (PRINTF_SPU)
|
|
printf("ppc4xx_spu_timer_reset: baud rate = %.0f\n", ATTOSECONDS_TO_HZ(charperiod.attoseconds) * bpc);
|
|
}
|
|
|
|
/* otherwise, disable the timer */
|
|
else
|
|
timer_adjust_oneshot(ppc->spu.timer, attotime_never, 0);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_spu_callback - serial port send/receive
|
|
timer
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( ppc4xx_spu_callback )
|
|
{
|
|
powerpc_state *ppc = ptr;
|
|
|
|
/* transmit enabled? */
|
|
if (ppc->spu.regs[SPU4XX_TX_COMMAND] & 0x80)
|
|
{
|
|
int operation = (ppc->spu.regs[SPU4XX_TX_COMMAND] >> 5) & 3;
|
|
|
|
/* if we have data to transmit, do it now */
|
|
if (!(ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x04))
|
|
{
|
|
/* if we have a transmit handler, send it that way */
|
|
if (ppc->spu.tx_handler != NULL)
|
|
(*ppc->spu.tx_handler)(ppc->spu.txbuf);
|
|
|
|
/* indicate that we have moved it to the shift register */
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] |= 0x04;
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] &= ~0x02;
|
|
}
|
|
|
|
/* otherwise, clear the shift register */
|
|
else if (!(ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x02))
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] |= 0x02;
|
|
|
|
/* handle DMA */
|
|
if (operation >= 2 && ppc4xx_dma_fetch_transmit_byte(ppc, operation, &ppc->spu.txbuf))
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] &= ~0x04;
|
|
}
|
|
|
|
/* receive enabled? */
|
|
if (ppc->spu.regs[SPU4XX_RX_COMMAND] & 0x80)
|
|
if (ppc->spu.rxout != ppc->spu.rxin)
|
|
{
|
|
int operation = (ppc->spu.regs[SPU4XX_RX_COMMAND] >> 5) & 3;
|
|
UINT8 rxbyte;
|
|
|
|
/* consume the byte and advance the out pointer */
|
|
rxbyte = ppc->spu.rxbuffer[ppc->spu.rxout];
|
|
ppc->spu.rxout = (ppc->spu.rxout + 1) % ARRAY_LENGTH(ppc->spu.rxbuffer);
|
|
|
|
/* if we're not full, copy data to the buffer and update the line status */
|
|
if (!(ppc->spu.regs[SPU4XX_LINE_STATUS] & 0x80))
|
|
{
|
|
ppc->spu.rxbuf = rxbyte;
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] |= 0x80;
|
|
}
|
|
|
|
/* otherwise signal an overrun */
|
|
else
|
|
{
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] |= 0x20;
|
|
goto updateirq;
|
|
}
|
|
|
|
/* handle DMA */
|
|
if (operation >= 2 && ppc4xx_dma_handle_receive_byte(ppc, operation, ppc->spu.rxbuf))
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] &= ~0x80;
|
|
}
|
|
|
|
/* update the final IRQ states */
|
|
updateirq:
|
|
ppc4xx_spu_update_irq_states(ppc);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_spu_r - serial port read handler
|
|
-------------------------------------------------*/
|
|
|
|
static READ8_HANDLER( ppc4xx_spu_r )
|
|
{
|
|
powerpc_state *ppc = cpu_get_info_ptr(space->cpu, CPUINFO_PTR_CONTEXT);
|
|
UINT8 result = 0xff;
|
|
|
|
switch (offset)
|
|
{
|
|
case SPU4XX_BUFFER:
|
|
result = ppc->spu.rxbuf;
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] &= ~0x80;
|
|
break;
|
|
|
|
default:
|
|
if (offset < ARRAY_LENGTH(ppc->spu.regs))
|
|
result = ppc->spu.regs[offset];
|
|
break;
|
|
}
|
|
if (PRINTF_SPU)
|
|
printf("spu_r(%d) = %02X\n", offset, result);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_spu_w - serial port write handler
|
|
-------------------------------------------------*/
|
|
|
|
static WRITE8_HANDLER( ppc4xx_spu_w )
|
|
{
|
|
powerpc_state *ppc = cpu_get_info_ptr(space->cpu, CPUINFO_PTR_CONTEXT);
|
|
UINT8 oldstate, newstate;
|
|
|
|
if (PRINTF_SPU)
|
|
printf("spu_w(%d) = %02X\n", offset, data);
|
|
switch (offset)
|
|
{
|
|
/* clear error bits */
|
|
case SPU4XX_LINE_STATUS:
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] &= ~(data & 0xf8);
|
|
ppc4xx_spu_update_irq_states(ppc);
|
|
break;
|
|
|
|
/* enable/disable the timer if one of these is enabled */
|
|
case SPU4XX_RX_COMMAND:
|
|
case SPU4XX_TX_COMMAND:
|
|
oldstate = ppc->spu.regs[SPU4XX_RX_COMMAND] | ppc->spu.regs[SPU4XX_TX_COMMAND];
|
|
ppc->spu.regs[offset] = data;
|
|
newstate = ppc->spu.regs[SPU4XX_RX_COMMAND] | ppc->spu.regs[SPU4XX_TX_COMMAND];
|
|
if ((oldstate ^ newstate) & 0x80)
|
|
ppc4xx_spu_timer_reset(ppc);
|
|
ppc4xx_spu_update_irq_states(ppc);
|
|
break;
|
|
|
|
/* if the divisor changes, we need to update the timer */
|
|
case SPU4XX_BAUD_DIVISOR_H:
|
|
case SPU4XX_BAUD_DIVISOR_L:
|
|
if (data != ppc->spu.regs[offset])
|
|
{
|
|
ppc->spu.regs[offset] = data;
|
|
ppc4xx_spu_timer_reset(ppc);
|
|
}
|
|
break;
|
|
|
|
/* if the number of data bits or stop bits changes, we need to update the timer */
|
|
case SPU4XX_CONTROL:
|
|
oldstate = ppc->spu.regs[offset];
|
|
ppc->spu.regs[offset] = data;
|
|
if ((oldstate ^ data) & 0x09)
|
|
ppc4xx_spu_timer_reset(ppc);
|
|
break;
|
|
|
|
break;
|
|
|
|
case SPU4XX_BUFFER:
|
|
/* write to the transmit buffer and mark it full */
|
|
ppc->spu.txbuf = data;
|
|
ppc->spu.regs[SPU4XX_LINE_STATUS] &= ~0x04;
|
|
break;
|
|
|
|
default:
|
|
if (offset < ARRAY_LENGTH(ppc->spu.regs))
|
|
ppc->spu.regs[offset] = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------
|
|
internal_ppc4xx - internal address map for
|
|
the 4XX
|
|
-------------------------------------------------*/
|
|
|
|
static ADDRESS_MAP_START( internal_ppc4xx, ADDRESS_SPACE_PROGRAM, 32 )
|
|
AM_RANGE(0x40000000, 0x4000000f) AM_READWRITE8(ppc4xx_spu_r, ppc4xx_spu_w, 0xffffffff)
|
|
ADDRESS_MAP_END
|
|
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_set_info - PowerPC 4XX-specific
|
|
information setter
|
|
-------------------------------------------------*/
|
|
|
|
void ppc4xx_set_info(powerpc_state *ppc, UINT32 state, cpuinfo *info)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_0: ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_EXT0, info->i); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_1: ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_EXT1, info->i); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_2: ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_EXT2, info->i); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_3: ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_EXT3, info->i); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_4: ppc4xx_set_irq_line(ppc, PPC4XX_IRQ_BIT_EXT4, info->i); break;
|
|
case CPUINFO_INT_PPC_RX_DATA: ppc4xx_spu_rx_data(ppc, info->i); break;
|
|
|
|
/* --- the following bits of info are returned as pointers to data or functions --- */
|
|
case CPUINFO_PTR_SPU_TX_HANDLER: ppc->spu.tx_handler = (ppc4xx_spu_tx_handler)info->f; break;
|
|
|
|
/* --- everything else is handled generically --- */
|
|
default: ppccom_set_info(ppc, state, info); break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
ppc4xx_get_info - PowerPC 4XX-specific
|
|
information getter
|
|
-------------------------------------------------*/
|
|
|
|
void ppc4xx_get_info(powerpc_state *ppc, UINT32 state, cpuinfo *info)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
|
case CPUINFO_INT_INPUT_LINES: info->i = 5; break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_0: info->i = ppc4xx_get_irq_line(ppc, PPC4XX_IRQ_BIT_EXT0); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_1: info->i = ppc4xx_get_irq_line(ppc, PPC4XX_IRQ_BIT_EXT1); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_2: info->i = ppc4xx_get_irq_line(ppc, PPC4XX_IRQ_BIT_EXT2); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_3: info->i = ppc4xx_get_irq_line(ppc, PPC4XX_IRQ_BIT_EXT3); break;
|
|
case CPUINFO_INT_INPUT_STATE + PPC_IRQ_LINE_4: info->i = ppc4xx_get_irq_line(ppc, PPC4XX_IRQ_BIT_EXT4); break;
|
|
|
|
case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32; break;
|
|
case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 31; break;
|
|
case CPUINFO_INT_LOGADDR_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32; break;
|
|
case CPUINFO_INT_PAGE_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = POWERPC_MIN_PAGE_SHIFT;break;
|
|
|
|
/* --- the following bits of info are returned as pointers to data or functions --- */
|
|
case CPUINFO_PTR_INIT: /* provided per-CPU */ break;
|
|
case CPUINFO_PTR_INTERNAL_MEMORY_MAP + ADDRESS_SPACE_PROGRAM: info->internal_map32 = ADDRESS_MAP_NAME(internal_ppc4xx); break;
|
|
|
|
/* --- everything else is handled generically --- */
|
|
default: ppccom_get_info(ppc, state, info); break;
|
|
}
|
|
}
|
|
|