mame/src/emu/cpu/powerpc/ppccom.c
Aaron Giles a5bf427929 Added "tag" parameter to state_save_register_item_* calls. Removed
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.
2008-11-17 06:21:26 +00:00

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;
}
}