diff options
Diffstat (limited to 'trunk/src/emu/cpu/powerpc/ppccom.c')
-rw-r--r-- | trunk/src/emu/cpu/powerpc/ppccom.c | 2242 |
1 files changed, 2242 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/powerpc/ppccom.c b/trunk/src/emu/cpu/powerpc/ppccom.c new file mode 100644 index 00000000000..fdc49bc567e --- /dev/null +++ b/trunk/src/emu/cpu/powerpc/ppccom.c @@ -0,0 +1,2242 @@ +/*************************************************************************** + + ppccom.c + + Common PowerPC definitions and functions + +***************************************************************************/ + +#include "emu.h" +#include "ppccom.h" + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define PRINTF_SPU (0) +#define PRINTF_DECREMENTER (0) + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define DOUBLE_SIGN (U64(0x8000000000000000)) +#define DOUBLE_EXP (U64(0x7ff0000000000000)) +#define DOUBLE_FRAC (U64(0x000fffffffffffff)) +#define DOUBLE_ZERO (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) +{ + if (!ppc->tb_divisor) + { + return (ppc->device->total_cycles() - ppc->tb_zero_cycles); + } + + return (ppc->device->total_cycles() - 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 = ppc->device->total_cycles() - 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 - ppc->device->total_cycles(); + cycles_until_zero = MAX(cycles_until_zero, 0); + + if (!ppc->tb_divisor) + { + return 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 (!ppc->tb_divisor) + { + return; + } + + if (PRINTF_DECREMENTER) + { + UINT64 total = ppc->device->total_cycles(); + 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 = ppc->device->total_cycles() + cycles_until_done; + ppc->decrementer_int_timer->adjust(ppc->device->cycles_to_attotime(cycles_until_done)); + + if ((INT32)curdec >= 0 && (INT32)newdec < 0) + ppc->irq_pending |= 0x02; +} + + +/*------------------------------------------------- + is_nan_double - is a double value a NaN +-------------------------------------------------*/ + +INLINE int is_nan_double(double x) +{ + UINT64 xi = *(UINT64*)&x; + return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) && + ((xi & DOUBLE_FRAC) != DOUBLE_ZERO) ); +} + + +/*------------------------------------------------- + is_qnan_double - is a double value a + quiet NaN +-------------------------------------------------*/ + +INLINE int is_qnan_double(double x) +{ + UINT64 xi = *(UINT64*)&x; + return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) && + ((xi & U64(0x0007fffffffffff)) == U64(0x000000000000000)) && + ((xi & U64(0x000800000000000)) == U64(0x000800000000000)) ); +} + + +/*------------------------------------------------- + is_snan_double - is a double value a + signaling NaN +-------------------------------------------------*/ + +INLINE int is_snan_double(double x) +{ + UINT64 xi = *(UINT64*)&x; + return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) && + ((xi & DOUBLE_FRAC) != DOUBLE_ZERO) && + ((xi & U64(0x0008000000000000)) == DOUBLE_ZERO) ); +} + + +/*------------------------------------------------- + is_infinity_double - is a double value + infinity +-------------------------------------------------*/ + +INLINE int is_infinity_double(double x) +{ + UINT64 xi = *(UINT64*)&x; + return( ((xi & DOUBLE_EXP) == DOUBLE_EXP) && + ((xi & DOUBLE_FRAC) == DOUBLE_ZERO) ); +} + + +/*------------------------------------------------- + is_normalized_double - is a double value + normalized +-------------------------------------------------*/ + +INLINE int is_normalized_double(double x) +{ + UINT64 exp; + UINT64 xi = *(UINT64*)&x; + exp = (xi & DOUBLE_EXP) >> 52; + + return (exp >= 1) && (exp <= 2046); +} + + +/*------------------------------------------------- + is_denormalized_double - is a double value + denormalized +-------------------------------------------------*/ + +INLINE int is_denormalized_double(double x) +{ + UINT64 xi = *(UINT64*)&x; + return( ((xi & DOUBLE_EXP) == 0) && + ((xi & DOUBLE_FRAC) != DOUBLE_ZERO) ); +} + + +/*------------------------------------------------- + sign_double - return sign of a double value +-------------------------------------------------*/ + +INLINE int sign_double(double x) +{ + UINT64 xi = *(UINT64*)&x; + return ((xi & DOUBLE_SIGN) != 0); +} + + + +/*************************************************************************** + 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, legacy_cpu_device *device, device_irq_acknowledge_callback irqcallback) +{ + const powerpc_config *config = (const powerpc_config *)device->static_config(); + + /* initialize based on the config */ + memset(ppc, 0, sizeof(*ppc)); + ppc->flavor = flavor; + ppc->cap = cap; + ppc->cache_line_size = 32; + ppc->tb_divisor = tb_divisor; + ppc->cpu_clock = device->clock(); + ppc->irq_callback = irqcallback; + ppc->device = device; + ppc->program = device->space(AS_PROGRAM); + ppc->direct = &ppc->program->direct(); + ppc->system_clock = (config != NULL) ? config->bus_frequency : device->clock(); + ppc->dcr_read_func = (config != NULL) ? config->dcr_read_func : NULL; + ppc->dcr_write_func = (config != NULL) ? config->dcr_write_func : NULL; + + ppc->tb_divisor = (ppc->tb_divisor * device->clock() + ppc->system_clock / 2 - 1) / ppc->system_clock; + ppc->codexor = 0; + if (!(cap & PPCCAP_4XX) && device->space_config()->m_endianness != ENDIANNESS_NATIVE) + ppc->codexor = 4; + + /* allocate the virtual TLB */ + ppc->vtlb = vtlb_alloc(device, AS_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->tb_divisor)) + ppc->decrementer_int_timer = device->machine().scheduler().timer_alloc(FUNC(decrementer_int_callback), ppc); + + /* and for the 4XX interrupts if needed */ + if (cap & PPCCAP_4XX) + { + ppc->fit_timer = device->machine().scheduler().timer_alloc(FUNC(ppc4xx_fit_callback), ppc); + ppc->pit_timer = device->machine().scheduler().timer_alloc(FUNC(ppc4xx_pit_callback), ppc); + ppc->spu.timer = device->machine().scheduler().timer_alloc(FUNC(ppc4xx_spu_callback), ppc); + } + + /* register for save states */ + device->save_item(NAME(ppc->pc)); + device->save_item(NAME(ppc->r)); + device->save_item(NAME(ppc->f)); + device->save_item(NAME(ppc->cr)); + device->save_item(NAME(ppc->xerso)); + device->save_item(NAME(ppc->fpscr)); + device->save_item(NAME(ppc->msr)); + device->save_item(NAME(ppc->sr)); + device->save_item(NAME(ppc->spr)); + device->save_item(NAME(ppc->dcr)); + if (cap & PPCCAP_4XX) + { + device->save_item(NAME(ppc->spu.regs)); + device->save_item(NAME(ppc->spu.txbuf)); + device->save_item(NAME(ppc->spu.rxbuf)); + device->save_item(NAME(ppc->spu.rxbuffer)); + device->save_item(NAME(ppc->spu.rxin)); + device->save_item(NAME(ppc->spu.rxout)); + device->save_item(NAME(ppc->pit_reload)); + device->save_item(NAME(ppc->irqstate)); + } + if (cap & PPCCAP_603_MMU) + { + device->save_item(NAME(ppc->mmu603_cmp)); + device->save_item(NAME(ppc->mmu603_hash)); + device->save_item(NAME(ppc->mmu603_r)); + } + device->save_item(NAME(ppc->irq_pending)); + device->save_item(NAME(ppc->tb_zero_cycles)); + device->save_item(NAME(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 = ppc->device->total_cycles(); + if (ppc->tb_divisor) + { + decrementer_int_callback(ppc->device->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 = ppc->device->total_cycles(); + + /* 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); // 1 for supervisor, 0 for user + 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 */ + if (ppc->cap & PPCCAP_601BAT) + { + for (batnum = 0; batnum < 4; batnum++) + { + UINT32 upper = ppc->spr[SPROEA_IBAT0U + 2*batnum + 0]; + UINT32 lower = ppc->spr[SPROEA_IBAT0U + 2*batnum + 1]; + int privbit = ((intention & TRANSLATE_USER_MASK) == 0) ? 3 : 2; + +// printf("bat %d upper = %08x privbit %d\n", batnum, upper, privbit); + + // is this pair valid? + if (lower & 0x40) + { + UINT32 mask = (~lower & 0x3f) << 17; + UINT32 addrout; + UINT32 key = (upper >> privbit) & 1; + + /* check for a hit against this bucket */ + if ((*address & 0xfffe0000) == (upper & 0xfffe0000)) + { + /* verify protection; if we fail, return false and indicate a protection violation */ + if (!page_access_allowed(transtype, key, upper & 3)) + { + return DSISR_PROTECTED | ((transtype == TRANSLATE_WRITE) ? DSISR_STORE : 0); + } + + /* otherwise we're good */ + addrout = (lower & 0xff100000) | (*address & ~0xfffe0000); + addrout |= ((*address & mask) | (lower & mask)); + *address = addrout; // top 9 bits from top 9 of PBN + return 0x001; + } + } + } + } + else + { + 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); + + /* check for memory-forced I/O */ + if (ppc->cap & PPCCAP_MFIOC) + { + if ((transtype != TRANSLATE_FETCH) && ((segreg & 0x87f00000) == 0x87f00000)) + { + *address = ((segreg & 0xf)<<28) | (*address & 0x0fffffff); + return 1; + } + else if (segreg & 0x80000000) + { + fatalerror("PPC: Unhandled segment register %08x with T=1\n", segreg); + } + } + + /* 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) + { + UINT32 entry = vtlb_table(ppc->vtlb)[*address >> 12]; + 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); + if ((entry & (VTLB_FLAG_FIXED | VTLB_FLAG_VALID)) == (VTLB_FLAG_FIXED | VTLB_FLAG_VALID)) + { + *address = (entry & 0xfffff000) | (*address & 0x00000fff); + return 0x001; + } + 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 = (UINT32 *)ppc->program->get_read_ptr(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, address_spacenum space, int intention, offs_t *address) +{ + /* only applies to the program address space */ + if (space != AS_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) | (ppc->device->machine().rand() & 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(ppc->device->machine(), ppc, FALSE); + if ((oldval ^ ppc->spr[SPR4XX_TCR]) & PPC4XX_TCR_PIE) + ppc4xx_pit_callback(ppc->device->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(ppc->device->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 */ + if (!ppc->dcr_read_func) { + 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; + } else { + ppc->param1 = ppc->dcr_read_func(ppc->device,ppc->param0,0xffffffff); + } +} + + +/*------------------------------------------------- + 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 */ + if (!ppc->dcr_write_func) { + 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; + } else { + ppc->dcr_write_func(ppc->device,ppc->param0,ppc->param1,0xffffffff); + } +} + + + +/*************************************************************************** + FLOATING POINT STATUS FLAGS HANDLING +***************************************************************************/ + +/*------------------------------------------------- + ppccom_update_fprf - update the FPRF field + of the FPSCR register +-------------------------------------------------*/ + +void ppccom_update_fprf(powerpc_state *ppc) +{ + UINT32 fprf; + double f = ppc->f[ppc->param0]; + + if (is_qnan_double(f)) + { + fprf = 0x11; + } + else if (is_infinity_double(f)) + { + if (sign_double(f)) /* -Infinity */ + fprf = 0x09; + else /* +Infinity */ + fprf = 0x05; + } + else if (is_normalized_double(f)) + { + if (sign_double(f)) /* -Normalized */ + fprf = 0x08; + else /* +Normalized */ + fprf = 0x04; + } + else if (is_denormalized_double(f)) + { + if (sign_double(f)) /* -Denormalized */ + fprf = 0x18; + else /* +Denormalized */ + fprf = 0x14; + } + else + { + if (sign_double(f)) /* -Zero */ + fprf = 0x12; + else /* +Zero */ + fprf = 0x02; + } + + ppc->fpscr &= ~0x0001f000; + ppc->fpscr |= fprf << 12; +} + + + +/*************************************************************************** + 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; + + case CPUINFO_INT_REGISTER + PPC_F0: ppc->f[0] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F1: ppc->f[1] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F2: ppc->f[2] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F3: ppc->f[3] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F4: ppc->f[4] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F5: ppc->f[5] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F6: ppc->f[6] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F7: ppc->f[7] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F8: ppc->f[8] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F9: ppc->f[9] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F10: ppc->f[10] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F11: ppc->f[11] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F12: ppc->f[12] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F13: ppc->f[13] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F14: ppc->f[14] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F15: ppc->f[15] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F16: ppc->f[16] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F17: ppc->f[17] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F18: ppc->f[18] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F19: ppc->f[19] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F20: ppc->f[20] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F21: ppc->f[21] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F22: ppc->f[22] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F23: ppc->f[23] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F24: ppc->f[24] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F25: ppc->f[25] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F26: ppc->f[26] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F27: ppc->f[27] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F28: ppc->f[28] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F29: ppc->f[29] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F30: ppc->f[30] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_F31: ppc->f[31] = *(double *)&info->i; break; + case CPUINFO_INT_REGISTER + PPC_FPSCR: ppc->fpscr = 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 DEVINFO_INT_ENDIANNESS: info->i = ENDIANNESS_BIG; 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 DEVINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 64; break; + case DEVINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: info->i = 32; break; + case DEVINFO_INT_ADDRBUS_SHIFT + AS_PROGRAM: info->i = 0; break; + case CPUINFO_INT_LOGADDR_WIDTH_PROGRAM: info->i = 32; break; + case CPUINFO_INT_PAGE_SHIFT_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; + + case CPUINFO_INT_REGISTER + PPC_F0: info->i = *(UINT64 *)&ppc->f[0]; break; + case CPUINFO_INT_REGISTER + PPC_F1: info->i = *(UINT64 *)&ppc->f[1]; break; + case CPUINFO_INT_REGISTER + PPC_F2: info->i = *(UINT64 *)&ppc->f[2]; break; + case CPUINFO_INT_REGISTER + PPC_F3: info->i = *(UINT64 *)&ppc->f[3]; break; + case CPUINFO_INT_REGISTER + PPC_F4: info->i = *(UINT64 *)&ppc->f[4]; break; + case CPUINFO_INT_REGISTER + PPC_F5: info->i = *(UINT64 *)&ppc->f[5]; break; + case CPUINFO_INT_REGISTER + PPC_F6: info->i = *(UINT64 *)&ppc->f[6]; break; + case CPUINFO_INT_REGISTER + PPC_F7: info->i = *(UINT64 *)&ppc->f[7]; break; + case CPUINFO_INT_REGISTER + PPC_F8: info->i = *(UINT64 *)&ppc->f[8]; break; + case CPUINFO_INT_REGISTER + PPC_F9: info->i = *(UINT64 *)&ppc->f[9]; break; + case CPUINFO_INT_REGISTER + PPC_F10: info->i = *(UINT64 *)&ppc->f[10]; break; + case CPUINFO_INT_REGISTER + PPC_F11: info->i = *(UINT64 *)&ppc->f[11]; break; + case CPUINFO_INT_REGISTER + PPC_F12: info->i = *(UINT64 *)&ppc->f[12]; break; + case CPUINFO_INT_REGISTER + PPC_F13: info->i = *(UINT64 *)&ppc->f[13]; break; + case CPUINFO_INT_REGISTER + PPC_F14: info->i = *(UINT64 *)&ppc->f[14]; break; + case CPUINFO_INT_REGISTER + PPC_F15: info->i = *(UINT64 *)&ppc->f[15]; break; + case CPUINFO_INT_REGISTER + PPC_F16: info->i = *(UINT64 *)&ppc->f[16]; break; + case CPUINFO_INT_REGISTER + PPC_F17: info->i = *(UINT64 *)&ppc->f[17]; break; + case CPUINFO_INT_REGISTER + PPC_F18: info->i = *(UINT64 *)&ppc->f[18]; break; + case CPUINFO_INT_REGISTER + PPC_F19: info->i = *(UINT64 *)&ppc->f[19]; break; + case CPUINFO_INT_REGISTER + PPC_F20: info->i = *(UINT64 *)&ppc->f[20]; break; + case CPUINFO_INT_REGISTER + PPC_F21: info->i = *(UINT64 *)&ppc->f[21]; break; + case CPUINFO_INT_REGISTER + PPC_F22: info->i = *(UINT64 *)&ppc->f[22]; break; + case CPUINFO_INT_REGISTER + PPC_F23: info->i = *(UINT64 *)&ppc->f[23]; break; + case CPUINFO_INT_REGISTER + PPC_F24: info->i = *(UINT64 *)&ppc->f[24]; break; + case CPUINFO_INT_REGISTER + PPC_F25: info->i = *(UINT64 *)&ppc->f[25]; break; + case CPUINFO_INT_REGISTER + PPC_F26: info->i = *(UINT64 *)&ppc->f[26]; break; + case CPUINFO_INT_REGISTER + PPC_F27: info->i = *(UINT64 *)&ppc->f[27]; break; + case CPUINFO_INT_REGISTER + PPC_F28: info->i = *(UINT64 *)&ppc->f[28]; break; + case CPUINFO_INT_REGISTER + PPC_F29: info->i = *(UINT64 *)&ppc->f[29]; break; + case CPUINFO_INT_REGISTER + PPC_F30: info->i = *(UINT64 *)&ppc->f[30]; break; + case CPUINFO_INT_REGISTER + PPC_F31: info->i = *(UINT64 *)&ppc->f[31]; break; + case CPUINFO_INT_REGISTER + PPC_FPSCR: info->i = ppc->fpscr; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case CPUINFO_FCT_SET_INFO: /* provided by core */ break; + case CPUINFO_FCT_INIT: /* provided by core */ break; + case CPUINFO_FCT_RESET: /* provided by core */ break; + case CPUINFO_FCT_EXIT: /* provided by core */ break; + case CPUINFO_FCT_EXECUTE: /* provided by core */ break; + case CPUINFO_FCT_TRANSLATE: /* provided by core */ break; + case CPUINFO_FCT_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 DEVINFO_STR_NAME: strcpy(info->s, "PowerPC"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "PowerPC"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "2.0"); break; + case DEVINFO_STR_SOURCE_FILE: /* provided by core */ break; + case DEVINFO_STR_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; + + case CPUINFO_STR_REGISTER + PPC_F0: sprintf(info->s, "F0: %12f", ppc->f[0]); break; + case CPUINFO_STR_REGISTER + PPC_F1: sprintf(info->s, "F1: %12f", ppc->f[1]); break; + case CPUINFO_STR_REGISTER + PPC_F2: sprintf(info->s, "F2: %12f", ppc->f[2]); break; + case CPUINFO_STR_REGISTER + PPC_F3: sprintf(info->s, "F3: %12f", ppc->f[3]); break; + case CPUINFO_STR_REGISTER + PPC_F4: sprintf(info->s, "F4: %12f", ppc->f[4]); break; + case CPUINFO_STR_REGISTER + PPC_F5: sprintf(info->s, "F5: %12f", ppc->f[5]); break; + case CPUINFO_STR_REGISTER + PPC_F6: sprintf(info->s, "F6: %12f", ppc->f[6]); break; + case CPUINFO_STR_REGISTER + PPC_F7: sprintf(info->s, "F7: %12f", ppc->f[7]); break; + case CPUINFO_STR_REGISTER + PPC_F8: sprintf(info->s, "F8: %12f", ppc->f[8]); break; + case CPUINFO_STR_REGISTER + PPC_F9: sprintf(info->s, "F9: %12f", ppc->f[9]); break; + case CPUINFO_STR_REGISTER + PPC_F10: sprintf(info->s, "F10:%12f", ppc->f[10]); break; + case CPUINFO_STR_REGISTER + PPC_F11: sprintf(info->s, "F11:%12f", ppc->f[11]); break; + case CPUINFO_STR_REGISTER + PPC_F12: sprintf(info->s, "F12:%12f", ppc->f[12]); break; + case CPUINFO_STR_REGISTER + PPC_F13: sprintf(info->s, "F13:%12f", ppc->f[13]); break; + case CPUINFO_STR_REGISTER + PPC_F14: sprintf(info->s, "F14:%12f", ppc->f[14]); break; + case CPUINFO_STR_REGISTER + PPC_F15: sprintf(info->s, "F15:%12f", ppc->f[15]); break; + case CPUINFO_STR_REGISTER + PPC_F16: sprintf(info->s, "F16:%12f", ppc->f[16]); break; + case CPUINFO_STR_REGISTER + PPC_F17: sprintf(info->s, "F17:%12f", ppc->f[17]); break; + case CPUINFO_STR_REGISTER + PPC_F18: sprintf(info->s, "F18:%12f", ppc->f[18]); break; + case CPUINFO_STR_REGISTER + PPC_F19: sprintf(info->s, "F19:%12f", ppc->f[19]); break; + case CPUINFO_STR_REGISTER + PPC_F20: sprintf(info->s, "F20:%12f", ppc->f[20]); break; + case CPUINFO_STR_REGISTER + PPC_F21: sprintf(info->s, "F21:%12f", ppc->f[21]); break; + case CPUINFO_STR_REGISTER + PPC_F22: sprintf(info->s, "F22:%12f", ppc->f[22]); break; + case CPUINFO_STR_REGISTER + PPC_F23: sprintf(info->s, "F23:%12f", ppc->f[23]); break; + case CPUINFO_STR_REGISTER + PPC_F24: sprintf(info->s, "F24:%12f", ppc->f[24]); break; + case CPUINFO_STR_REGISTER + PPC_F25: sprintf(info->s, "F25:%12f", ppc->f[25]); break; + case CPUINFO_STR_REGISTER + PPC_F26: sprintf(info->s, "F26:%12f", ppc->f[26]); break; + case CPUINFO_STR_REGISTER + PPC_F27: sprintf(info->s, "F27:%12f", ppc->f[27]); break; + case CPUINFO_STR_REGISTER + PPC_F28: sprintf(info->s, "F28:%12f", ppc->f[28]); break; + case CPUINFO_STR_REGISTER + PPC_F29: sprintf(info->s, "F29:%12f", ppc->f[29]); break; + case CPUINFO_STR_REGISTER + PPC_F30: sprintf(info->s, "F30:%12f", ppc->f[30]); break; + case CPUINFO_STR_REGISTER + PPC_F31: sprintf(info->s, "F31:%12f", ppc->f[31]); break; + case CPUINFO_STR_REGISTER + PPC_FPSCR: sprintf(info->s, "FPSCR:%08X", ppc->fpscr); break; + } +} + + + +/*************************************************************************** + OEA HELPERS +***************************************************************************/ + +/*------------------------------------------------- + decrementer_int_callback - callback that fires + whenever a decrementer interrupt is generated +-------------------------------------------------*/ + +static TIMER_CALLBACK( decrementer_int_callback ) +{ + powerpc_state *ppc = (powerpc_state *)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 - ppc->device->total_cycles(); + ppc->decrementer_int_timer->adjust(ppc->device->cycles_to_attotime(cycles_until_next)); +} + + + +/*************************************************************************** + 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 */ + *byte = ppc->program->read_byte(dmaregs[DCR4XX_DMADA0]++); + ppc4xx_dma_decrement_count(ppc, dmachan); + 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 */ + ppc->program->write_byte(dmaregs[DCR4XX_DMADA0]++, byte); + ppc4xx_dma_decrement_count(ppc, dmachan); + 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 + { + ppc->program->write_byte(dmaregs[DCR4XX_DMADA0], ppc->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 + { + ppc->program->write_word(dmaregs[DCR4XX_DMADA0], ppc->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 + { + ppc->program->write_dword(dmaregs[DCR4XX_DMADA0], ppc->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 + { + ppc->program->write_qword(dmaregs[DCR4XX_DMADA0], ppc->program->read_qword(dmaregs[DCR4XX_DMASA0])); + ppc->program->write_qword(dmaregs[DCR4XX_DMADA0] + 8, ppc->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 = (powerpc_state *)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); + ppc->fit_timer->adjust(ppc->device->cycles_to_attotime((target + 1 - timebase) / ppc->tb_divisor), TRUE); + } + + /* otherwise, turn ourself off */ + else + ppc->fit_timer->adjust(attotime::never, FALSE); +} + + +/*------------------------------------------------- + ppc4xx_pit_callback - PIT timer callback +-------------------------------------------------*/ + +static TIMER_CALLBACK( ppc4xx_pit_callback ) +{ + powerpc_state *ppc = (powerpc_state *)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; + ppc->pit_timer->adjust(ppc->device->cycles_to_attotime((target + 1 - timebase) / ppc->tb_divisor), TRUE); + } + + /* otherwise, turn ourself off */ + else + ppc->pit_timer->adjust(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::from_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 = clockperiod * (divisor * 16 * bpc); + ppc->spu.timer->adjust(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 + ppc->spu.timer->adjust(attotime::never); +} + + +/*------------------------------------------------- + ppc4xx_spu_callback - serial port send/receive + timer +-------------------------------------------------*/ + +static TIMER_CALLBACK( ppc4xx_spu_callback ) +{ + powerpc_state *ppc = (powerpc_state *)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->device, 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 = *(powerpc_state **)downcast<legacy_cpu_device *>(&space->device())->token(); + 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 = *(powerpc_state **)downcast<legacy_cpu_device *>(&space->device())->token(); + 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, AS_PROGRAM, 32, legacy_cpu_device ) + AM_RANGE(0x40000000, 0x4000000f) AM_READWRITE8_LEGACY(ppc4xx_spu_r, ppc4xx_spu_w, 0xffffffff) +ADDRESS_MAP_END + + + +/*------------------------------------------------- + ppc4xx_spu_set_tx_handler - PowerPC 4XX- + specific TX handler configuration +-------------------------------------------------*/ + +void ppc4xx_spu_set_tx_handler(device_t *device, ppc4xx_spu_tx_handler handler) +{ + powerpc_state *ppc = *(powerpc_state **)downcast<legacy_cpu_device *>(device)->token(); + ppc->spu.tx_handler = handler; +} + + +/*------------------------------------------------- + ppc4xx_spu_receive_byte - PowerPC 4XX- + specific serial byte receive +-------------------------------------------------*/ + +void ppc4xx_spu_receive_byte(device_t *device, UINT8 byteval) +{ + powerpc_state *ppc = *(powerpc_state **)downcast<legacy_cpu_device *>(device)->token(); + ppc4xx_spu_rx_data(ppc, byteval); +} + + +/*------------------------------------------------- + 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; + + /* --- 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 DEVINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 32; break; + case DEVINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: info->i = 31; break; + case CPUINFO_INT_LOGADDR_WIDTH_PROGRAM: info->i = 32; break; + case CPUINFO_INT_PAGE_SHIFT_PROGRAM: info->i = POWERPC_MIN_PAGE_SHIFT;break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case CPUINFO_FCT_INIT: /* provided per-CPU */ break; + case DEVINFO_PTR_INTERNAL_MEMORY_MAP + AS_PROGRAM: info->internal_map32 = ADDRESS_MAP_NAME(internal_ppc4xx); break; + + /* --- everything else is handled generically --- */ + default: ppccom_get_info(ppc, state, info); break; + } +} + |