mirror of
https://github.com/holub/mame
synced 2025-06-01 02:21:48 +03:00

the scheduler, rather than manging an incoming cycle count. It was confusing to have multiple sources of cycle counts.
954 lines
21 KiB
C
954 lines
21 KiB
C
/* PowerPC 403 specific functions */
|
|
|
|
static void ppc403_dma_exec(int ch);
|
|
|
|
#define DMA_CE 0x80000000
|
|
#define DMA_CIE 0x40000000
|
|
#define DMA_TD 0x20000000
|
|
#define DMA_PL 0x10000000
|
|
#define DMA_DAI 0x02000000
|
|
#define DMA_SAI 0x01000000
|
|
#define DMA_CP 0x00800000
|
|
#define DMA_ETD 0x00000200
|
|
#define DMA_TCE 0x00000100
|
|
#define DMA_CH 0x00000080
|
|
#define DMA_BME 0x00000040
|
|
#define DMA_ECE 0x00000020
|
|
#define DMA_TCD 0x00000010
|
|
#define DMA_PCE 0x00000008
|
|
|
|
static SPU_RX_HANDLER spu_rx_handler;
|
|
static SPU_TX_HANDLER spu_tx_handler;
|
|
static TIMER_CALLBACK( ppc403_spu_rx_callback );
|
|
static TIMER_CALLBACK( ppc403_spu_tx_callback );
|
|
|
|
static PPC_DMA_HANDLER spu_rx_dma_handler;
|
|
static PPC_DMA_HANDLER spu_tx_dma_handler;
|
|
static UINT8 *spu_rx_dma_ptr;
|
|
static UINT8 *spu_tx_dma_ptr;
|
|
|
|
static PPC_DMA_HANDLER dma_read_handler[4];
|
|
static PPC_DMA_HANDLER dma_write_handler[4];
|
|
static UINT8 *dma_read_ptr[4];
|
|
static UINT8 *dma_write_ptr[4];
|
|
|
|
INLINE void ppc_set_dcr(int dcr, UINT32 value)
|
|
{
|
|
switch(dcr)
|
|
{
|
|
case DCR_BEAR: ppc.bear = value; break;
|
|
case DCR_BESR: ppc.besr = value; break;
|
|
case DCR_BR0: ppc.br[0] = value; break;
|
|
case DCR_BR1: ppc.br[1] = value; break;
|
|
case DCR_BR2: ppc.br[2] = value; break;
|
|
case DCR_BR3: ppc.br[3] = value; break;
|
|
case DCR_BR4: ppc.br[4] = value; break;
|
|
case DCR_BR5: ppc.br[5] = value; break;
|
|
case DCR_BR6: ppc.br[6] = value; break;
|
|
case DCR_BR7: ppc.br[7] = value; break;
|
|
|
|
case DCR_EXISR: ppc.exisr &= ~value; break;
|
|
case DCR_EXIER: EXIER = value; ppc.exisr &= EXIER; break;
|
|
case DCR_IOCR: ppc.iocr = value; break;
|
|
case DCR_DMASR: break; /* TODO */
|
|
case DCR_DMADA0: ppc.dma[0].da = value; break;
|
|
case DCR_DMADA1: ppc.dma[1].da = value; break;
|
|
case DCR_DMADA2: ppc.dma[2].da = value; break;
|
|
case DCR_DMADA3: ppc.dma[3].da = value; break;
|
|
case DCR_DMASA0: ppc.dma[0].sa = value; break;
|
|
case DCR_DMASA1: ppc.dma[1].sa = value; break;
|
|
case DCR_DMASA2: ppc.dma[2].sa = value; break;
|
|
case DCR_DMASA3: ppc.dma[3].sa = value; break;
|
|
case DCR_DMACT0: ppc.dma[0].ct = value; break;
|
|
case DCR_DMACT1: ppc.dma[1].ct = value; break;
|
|
case DCR_DMACT2: ppc.dma[2].ct = value; break;
|
|
case DCR_DMACT3: ppc.dma[3].ct = value; break;
|
|
case DCR_DMACR0: ppc.dma[0].cr = value; ppc403_dma_exec(0); break;
|
|
case DCR_DMACR1: ppc.dma[1].cr = value; ppc403_dma_exec(1); break;
|
|
case DCR_DMACR2: ppc.dma[2].cr = value; ppc403_dma_exec(2); break;
|
|
case DCR_DMACR3: ppc.dma[3].cr = value; ppc403_dma_exec(3); break;
|
|
|
|
default:
|
|
fatalerror("ppc: set_dcr: Unimplemented DCR %X", dcr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
INLINE UINT32 ppc_get_dcr(int dcr)
|
|
{
|
|
switch(dcr)
|
|
{
|
|
case DCR_BEAR: return ppc.bear;
|
|
case DCR_BESR: return ppc.besr;
|
|
case DCR_BR0: return ppc.br[0];
|
|
case DCR_BR1: return ppc.br[1];
|
|
case DCR_BR2: return ppc.br[2];
|
|
case DCR_BR3: return ppc.br[3];
|
|
case DCR_BR4: return ppc.br[4];
|
|
case DCR_BR5: return ppc.br[5];
|
|
case DCR_BR6: return ppc.br[6];
|
|
case DCR_BR7: return ppc.br[7];
|
|
case DCR_EXISR: return EXISR;
|
|
case DCR_EXIER: return EXIER;
|
|
case DCR_IOCR: return ppc.iocr;
|
|
case DCR_DMASR: return ppc.dmasr;
|
|
case DCR_DMADA0: return ppc.dma[0].da;
|
|
case DCR_DMADA1: return ppc.dma[1].da;
|
|
case DCR_DMADA2: return ppc.dma[2].da;
|
|
case DCR_DMADA3: return ppc.dma[3].da;
|
|
case DCR_DMASA0: return ppc.dma[0].sa;
|
|
case DCR_DMASA1: return ppc.dma[1].sa;
|
|
case DCR_DMASA2: return ppc.dma[2].sa;
|
|
case DCR_DMASA3: return ppc.dma[3].sa;
|
|
case DCR_DMACT0: return ppc.dma[0].ct;
|
|
case DCR_DMACT1: return ppc.dma[1].ct;
|
|
case DCR_DMACT2: return ppc.dma[2].ct;
|
|
case DCR_DMACT3: return ppc.dma[3].ct;
|
|
case DCR_DMACR0: return ppc.dma[0].cr;
|
|
case DCR_DMACR1: return ppc.dma[1].cr;
|
|
case DCR_DMACR2: return ppc.dma[2].cr;
|
|
case DCR_DMACR3: return ppc.dma[3].cr;
|
|
|
|
default:
|
|
fatalerror("ppc: get_dcr: Unimplemented DCR %X", dcr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifndef PPC_DRC
|
|
INLINE void ppc403_check_interrupts(void)
|
|
{
|
|
if (MSR & MSR_EE)
|
|
{
|
|
if (ppc.interrupt_pending != 0)
|
|
{
|
|
if (ppc.interrupt_pending & 0x1)
|
|
{
|
|
ppc403_exception(EXCEPTION_IRQ);
|
|
}
|
|
else if (ppc.interrupt_pending & 0x2)
|
|
{
|
|
ppc403_exception(EXCEPTION_PROGRAMMABLE_INTERVAL_TIMER);
|
|
}
|
|
else if (ppc.interrupt_pending & 0x4)
|
|
{
|
|
ppc403_exception(EXCEPTION_FIXED_INTERVAL_TIMER);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static CPU_RESET( ppc403 )
|
|
{
|
|
ppc.pc = ppc.npc = 0xfffffffc;
|
|
|
|
ppc_set_msr(0);
|
|
}
|
|
|
|
static CPU_EXECUTE( ppc403 )
|
|
{
|
|
UINT32 fit_trigger_cycle;
|
|
ppc_tb_base_icount = cycles;
|
|
|
|
fit_trigger_cycle = 0x7fffffff;
|
|
|
|
if (ppc.fit_int_enable)
|
|
{
|
|
UINT32 tb = (UINT32)ppc.tb;
|
|
UINT32 fit_cycles = 0;
|
|
|
|
if (ppc.tb & ppc.fit_bit)
|
|
{
|
|
fit_cycles += ppc.fit_bit;
|
|
tb += fit_cycles;
|
|
}
|
|
|
|
fit_cycles += ppc.fit_bit - (tb & (ppc.fit_bit-1));
|
|
|
|
fit_trigger_cycle = ppc_icount - fit_cycles;
|
|
}
|
|
|
|
while( ppc_icount > 0 )
|
|
{
|
|
UINT32 opcode;
|
|
|
|
debugger_instruction_hook(device, ppc.pc);
|
|
ppc.pc = ppc.npc;
|
|
ppc.npc += 4;
|
|
opcode = ROPCODE(ppc.pc);
|
|
|
|
switch(opcode >> 26)
|
|
{
|
|
case 19: ppc.optable19[(opcode >> 1) & 0x3ff](opcode); break;
|
|
case 31: ppc.optable31[(opcode >> 1) & 0x3ff](opcode); break;
|
|
case 59: ppc.optable59[(opcode >> 1) & 0x3ff](opcode); break;
|
|
case 63: ppc.optable63[(opcode >> 1) & 0x3ff](opcode); break;
|
|
default: ppc.optable[opcode >> 26](opcode); break;
|
|
}
|
|
|
|
ppc_icount--;
|
|
|
|
/* Programmable Interval Timer (PIT) */
|
|
if (ppc.pit_counter > 0)
|
|
{
|
|
ppc.pit_counter--;
|
|
if (ppc.pit_counter == 0)
|
|
{
|
|
if (ppc.pit_int_enable) {
|
|
ppc.interrupt_pending |= 0x2;
|
|
}
|
|
if (ppc.tcr & 0x00400000) // Automatic reload
|
|
{
|
|
ppc.pit_counter = ppc.pit;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Fixed Interval Timer */
|
|
if (fit_trigger_cycle != 0x7fffffff)
|
|
{
|
|
if (ppc_icount == fit_trigger_cycle)
|
|
{
|
|
if (ppc.fit_int_enable)
|
|
{
|
|
fit_trigger_cycle -= ppc.fit_bit;
|
|
ppc.interrupt_pending |= 0x4;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* Watchdog Timer */
|
|
if (((UINT32)(ppc.tb) & ppc.wdt_bit) && (tblo & ppc.wdt_bit) == 0) {
|
|
switch((ppc.tsr >> 28) & 0x3)
|
|
{
|
|
case 0: ppc.tsr |= TSR_ENW; break;
|
|
case 1: ppc.tsr |= TSR_ENW; break;
|
|
case 2:
|
|
if (ppc.wdt_int_enable && (ppc.msr & MSR_CE)) {
|
|
ppc403_exception(EXCEPTION_WATCHDOG_TIMER);
|
|
}
|
|
break;
|
|
case 3:
|
|
fatalerror("PPC: Watchdog Timer caused reset");
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ppc403_check_interrupts();
|
|
}
|
|
|
|
// update timebase
|
|
ppc.tb += (ppc_tb_base_icount - ppc_icount);
|
|
}
|
|
|
|
void ppc403_exception(int exception)
|
|
{
|
|
switch( exception )
|
|
{
|
|
case EXCEPTION_IRQ: /* External Interrupt */
|
|
{
|
|
if( ppc_get_msr() & MSR_EE ) {
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.npc;
|
|
SRR1 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_EE | MSR_PE); // Clear WE, PR, EE, PR
|
|
if( msr & MSR_LE )
|
|
msr |= MSR_ILE;
|
|
else
|
|
msr &= ~MSR_ILE;
|
|
ppc_set_msr(msr);
|
|
|
|
ppc.npc = EVPR | 0x0500;
|
|
|
|
ppc.interrupt_pending &= ~0x1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_TRAP: /* Program exception / Trap */
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.pc;
|
|
SRR1 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_EE | MSR_PE); // Clear WE, PR, EE, PR
|
|
if( msr & MSR_ILE )
|
|
msr |= MSR_LE;
|
|
else
|
|
msr &= ~MSR_LE;
|
|
ppc_set_msr(msr);
|
|
|
|
if( msr & MSR_IP )
|
|
ppc.npc = 0xfff00000 | 0x0700;
|
|
else
|
|
ppc.npc = EVPR | 0x0700;
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_SYSTEM_CALL: /* System call */
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.npc;
|
|
SRR1 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_EE | MSR_PE); // Clear WE, PR, EE, PR
|
|
if( msr & MSR_ILE )
|
|
msr |= MSR_LE;
|
|
else
|
|
msr &= ~MSR_LE;
|
|
ppc_set_msr(msr);
|
|
|
|
if( msr & MSR_IP )
|
|
ppc.npc = 0xfff00000 | 0x0c00;
|
|
else
|
|
ppc.npc = EVPR | 0x0c00;
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_PROGRAMMABLE_INTERVAL_TIMER:
|
|
{
|
|
if( ppc_get_msr() & MSR_EE ) {
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.npc;
|
|
SRR1 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_EE | MSR_PE); // Clear WE, PR, EE, PR
|
|
if( msr & MSR_LE )
|
|
msr |= MSR_ILE;
|
|
else
|
|
msr &= ~MSR_ILE;
|
|
ppc_set_msr(msr);
|
|
|
|
ppc.npc = EVPR | 0x1000;
|
|
|
|
ppc.tsr |= 0x08000000; // PIT interrupt
|
|
ppc.interrupt_pending &= ~0x2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_FIXED_INTERVAL_TIMER: /* Fixed Interval Timer */
|
|
{
|
|
if( ppc_get_msr() & MSR_EE ) {
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.npc;
|
|
SRR1 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_EE | MSR_PE); // Clear WE, PR, EE, PR
|
|
if( msr & MSR_LE )
|
|
msr |= MSR_ILE;
|
|
else
|
|
msr &= ~MSR_ILE;
|
|
ppc_set_msr(msr);
|
|
|
|
ppc.npc = EVPR | 0x1010;
|
|
ppc.interrupt_pending &= ~0x4;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_WATCHDOG_TIMER: /* Watchdog Timer */
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR2 = ppc.npc;
|
|
SRR3 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_CE | MSR_EE | MSR_DE | MSR_PE | MSR_DR | MSR_IR);
|
|
if (msr & MSR_LE)
|
|
msr |= MSR_ILE;
|
|
else
|
|
msr &= ~MSR_ILE;
|
|
ppc_set_msr(msr);
|
|
|
|
ppc.npc = EVPR | 0x1020;
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_CRITICAL_INTERRUPT:
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR2 = ppc.npc;
|
|
SRR3 = msr;
|
|
|
|
msr &= ~(MSR_WE | MSR_PR | MSR_CE | MSR_EE | MSR_DE | MSR_PE | MSR_DR | MSR_IR);
|
|
if (msr & MSR_LE)
|
|
msr |= MSR_ILE;
|
|
else
|
|
msr &= ~MSR_ILE;
|
|
ppc_set_msr(msr);
|
|
|
|
EXISR |= 0x80000000;
|
|
ppc.npc = EVPR | 0x100;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fatalerror("ppc: Unhandled exception %d", exception);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ppc403_set_irq_line(int irqline, int state)
|
|
{
|
|
if (irqline >= INPUT_LINE_IRQ0 && irqline <= INPUT_LINE_IRQ4)
|
|
{
|
|
UINT32 mask = (1 << (4 - irqline));
|
|
if( state == ASSERT_LINE) {
|
|
if( EXIER & mask ) {
|
|
ppc.exisr |= mask;
|
|
ppc.interrupt_pending |= 0x1;
|
|
|
|
if (ppc.irq_callback)
|
|
{
|
|
ppc.irq_callback(ppc.device, irqline);
|
|
}
|
|
}
|
|
}
|
|
// clear line is used to clear the interrupt when the interrupts are level-sensitive
|
|
else if (state == CLEAR_LINE)
|
|
{
|
|
ppc.exisr &= ~mask;
|
|
}
|
|
}
|
|
else if (irqline == PPC_IRQ_SPU_RX)
|
|
{
|
|
UINT32 mask = 0x08000000;
|
|
if (state) {
|
|
if( EXIER & mask ) {
|
|
ppc.exisr |= mask;
|
|
ppc.interrupt_pending |= 0x1;
|
|
}
|
|
}
|
|
}
|
|
else if (irqline == PPC_IRQ_SPU_TX)
|
|
{
|
|
UINT32 mask = 0x04000000;
|
|
if (state) {
|
|
if( EXIER & mask ) {
|
|
ppc.exisr |= mask;
|
|
ppc.interrupt_pending |= 0x1;
|
|
}
|
|
}
|
|
}
|
|
else if (irqline == PPC_IRQ_CRITICAL)
|
|
{
|
|
if (state) {
|
|
if (EXIER & 0x80000000) {
|
|
ppc403_exception(EXCEPTION_CRITICAL_INTERRUPT);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fatalerror("PPC: Unknown IRQ line %d", irqline);
|
|
}
|
|
}
|
|
|
|
static void ppc403_dma_set_irq_line(int dma, int state)
|
|
{
|
|
UINT32 mask = (1 << (3 - dma)) << 20;
|
|
if( state ) {
|
|
if( EXIER & mask ) {
|
|
ppc.exisr |= mask;
|
|
ppc.interrupt_pending |= 0x1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef PPC_DRC
|
|
static void ppc403_dma_set_irq_line(int dma, int state)
|
|
{
|
|
UINT32 mask = (1 << (3 - dma)) << 20;
|
|
if( state ) {
|
|
if( EXIER & mask ) {
|
|
ppc.exisr |= mask;
|
|
ppc.exception_pending |= 0x1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef PPC_DRC
|
|
static void ppc_dccci(UINT32 op)
|
|
{
|
|
|
|
}
|
|
|
|
static void ppc_dcread(UINT32 op)
|
|
{
|
|
|
|
}
|
|
|
|
static void ppc_icbt(UINT32 op)
|
|
{
|
|
|
|
}
|
|
|
|
static void ppc_iccci(UINT32 op)
|
|
{
|
|
|
|
}
|
|
|
|
static void ppc_icread(UINT32 op)
|
|
{
|
|
|
|
}
|
|
|
|
static void ppc_rfci(UINT32 op)
|
|
{
|
|
UINT32 msr;
|
|
ppc.npc = ppc.srr2;
|
|
msr = ppc.srr3;
|
|
ppc_set_msr( msr );
|
|
|
|
}
|
|
#endif
|
|
|
|
static void ppc_mfdcr(UINT32 op)
|
|
{
|
|
REG(RT) = ppc_get_dcr(SPR);
|
|
}
|
|
|
|
static void ppc_mtdcr(UINT32 op)
|
|
{
|
|
ppc_set_dcr(SPR, REG(RS));
|
|
}
|
|
|
|
static void ppc_wrtee(UINT32 op)
|
|
{
|
|
if( REG(RS) & 0x8000 )
|
|
ppc_set_msr( ppc_get_msr() | MSR_EE);
|
|
else
|
|
ppc_set_msr( ppc_get_msr() & ~MSR_EE);
|
|
}
|
|
|
|
static void ppc_wrteei(UINT32 op)
|
|
{
|
|
if( op & 0x8000 )
|
|
ppc_set_msr( ppc_get_msr() | MSR_EE);
|
|
else
|
|
ppc_set_msr( ppc_get_msr() & ~MSR_EE);
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
/* PPC403 Serial Port */
|
|
|
|
static UINT8 ppc403_spu_r(UINT32 a)
|
|
{
|
|
switch(a & 0xf)
|
|
{
|
|
case 0x0: return ppc.spu.spls | 0x6; /* transmit buffer is always empty */
|
|
case 0x2: return ppc.spu.sphs;
|
|
case 0x4: return (ppc.spu.brd >> 8) & 0xf;
|
|
case 0x5: return (ppc.spu.brd & 0xff);
|
|
case 0x6: return ppc.spu.spctl;
|
|
case 0x7: return ppc.spu.sprc;
|
|
case 0x8: return ppc.spu.sptc;
|
|
case 0x9: return ppc.spu.sprb;
|
|
default: fatalerror("ppc: spu_r: %02X", a & 0xf);
|
|
}
|
|
}
|
|
|
|
static void ppc403_spu_w(UINT32 a, UINT8 d)
|
|
{
|
|
switch(a & 0xf)
|
|
{
|
|
case 0x0:
|
|
if( d & 0x80 ) ppc.spu.spls &= ~0x80;
|
|
if( d & 0x40 ) ppc.spu.spls &= ~0x40;
|
|
if( d & 0x20 ) ppc.spu.spls &= ~0x20;
|
|
if( d & 0x10 ) ppc.spu.spls &= ~0x10;
|
|
if( d & 0x08 ) ppc.spu.spls &= ~0x08;
|
|
break;
|
|
|
|
case 0x2:
|
|
ppc.spu.sphs = d;
|
|
break;
|
|
|
|
case 0x4:
|
|
ppc.spu.brd &= 0xff;
|
|
ppc.spu.brd |= (d << 8);
|
|
break;
|
|
|
|
case 0x5:
|
|
ppc.spu.brd &= 0xff00;
|
|
ppc.spu.brd |= d;
|
|
if (ppc.iocr & 0x2) {
|
|
mame_printf_debug("ppc: SPU Baud rate: %d\n", (3686400 / (ppc.spu.brd + 1)) / 16);
|
|
} else {
|
|
mame_printf_debug("ppc: SPU Baud rate: %d\n", (33333333 / (ppc.spu.brd + 1)) / 16);
|
|
}
|
|
break;
|
|
|
|
case 0x6:
|
|
ppc.spu.spctl = d;
|
|
break;
|
|
|
|
case 0x7:
|
|
ppc.spu.sprc = d;
|
|
if (ppc.spu.sprc & 0x80) /* enable RX */
|
|
{
|
|
/*
|
|
int baud_rate;
|
|
if (ppc.iocr & 0x2) {
|
|
baud_rate = (3686400 / (ppc.spu.brd + 1)) / 16;
|
|
} else {
|
|
baud_rate = (33333333 / (ppc.spu.brd + 1)) / 16;
|
|
}
|
|
*/
|
|
|
|
/* check if serial port is hooked to a DMA channel */
|
|
/* if so, do a DMA operation */
|
|
if( ((((ppc.spu.sprc >> 5) & 0x3) == 2) && (ppc.dma[2].cr & DMA_CE)) ||
|
|
((((ppc.spu.sprc >> 5) & 0x3) == 3) && (ppc.dma[3].cr & DMA_CE)) )
|
|
{
|
|
int i;
|
|
int ch = (ppc.spu.sprc >> 5) & 0x3;
|
|
// mame_printf_debug("ppc: DMA from serial port on channel %d (DA: %08X)\n", ch, ppc.dma[ch].da);
|
|
|
|
if (spu_rx_dma_handler)
|
|
{
|
|
int length = ppc.dma[ch].ct;
|
|
|
|
spu_rx_dma_handler(length);
|
|
|
|
for (i=0; i < length; i++)
|
|
{
|
|
memory_write_byte_32be(ppc.program, ppc.dma[ch].da++, spu_rx_dma_ptr[i]);
|
|
}
|
|
}
|
|
|
|
ppc.dmasr |= (1 << (27 - ch));
|
|
|
|
/* generate interrupts */
|
|
if( ppc.dma[ch].cr & DMA_CIE )
|
|
{
|
|
ppc403_dma_set_irq_line( ch, PULSE_LINE );
|
|
}
|
|
|
|
/* set receive buffer full */
|
|
ppc.spu.spls = 0x80;
|
|
|
|
#ifndef PPC_DRC
|
|
ppc403_set_irq_line(PPC_IRQ_SPU_RX, ASSERT_LINE);
|
|
#else
|
|
ppcdrc403_set_irq_line(PPC_IRQ_SPU_RX, ASSERT_LINE);
|
|
#endif
|
|
}
|
|
}
|
|
else /* disable RX */
|
|
{
|
|
}
|
|
break;
|
|
|
|
case 0x8:
|
|
ppc.spu.sptc = d;
|
|
break;
|
|
|
|
case 0x9:
|
|
ppc.spu.sptb = d;
|
|
ppc403_spu_tx_callback(NULL/* Machine */, NULL, cpunum_get_active());
|
|
break;
|
|
|
|
default:
|
|
fatalerror("ppc: spu_w: %02X, %02X", a & 0xf, d);
|
|
break;
|
|
}
|
|
//mame_printf_debug("spu_w: %02X, %02X at %08X\n", a & 0xf, d, ppc.pc);
|
|
}
|
|
|
|
void ppc403_spu_rx(UINT8 data)
|
|
{
|
|
ppc.spu.sprb = data;
|
|
|
|
/* set receive buffer full */
|
|
ppc.spu.spls = 0x80;
|
|
|
|
/* generate interrupt if DMA is disabled and RBR interrupt is enabled */
|
|
if (((ppc.spu.sprc >> 5) & 0x3) == 0x01) {
|
|
#ifndef PPC_DRC
|
|
ppc403_set_irq_line(PPC_IRQ_SPU_RX, ASSERT_LINE);
|
|
#else
|
|
ppcdrc403_set_irq_line(PPC_IRQ_SPU_RX, ASSERT_LINE);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static TIMER_CALLBACK( ppc403_spu_rx_callback )
|
|
{
|
|
if (spu_rx_handler != NULL)
|
|
{
|
|
ppc403_spu_rx(spu_rx_handler());
|
|
}
|
|
}
|
|
|
|
static TIMER_CALLBACK( ppc403_spu_tx_callback )
|
|
{
|
|
if (spu_tx_handler != NULL)
|
|
{
|
|
spu_tx_handler(ppc.spu.sptb);
|
|
|
|
/* generate interrupt if DMA is disabled and TBR interrupt is enabled */
|
|
if (((ppc.spu.sptc >> 5) & 0x3) == 0x01) {
|
|
#ifndef PPC_DRC
|
|
ppc403_set_irq_line(PPC_IRQ_SPU_TX, ASSERT_LINE);
|
|
#else
|
|
ppcdrc403_set_irq_line(PPC_IRQ_SPU_TX, ASSERT_LINE);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void ppc403_install_spu_rx_handler(SPU_RX_HANDLER rx_handler)
|
|
{
|
|
spu_rx_handler = rx_handler;
|
|
}
|
|
|
|
void ppc403_install_spu_tx_handler(SPU_TX_HANDLER tx_handler)
|
|
{
|
|
spu_tx_handler = tx_handler;
|
|
}
|
|
|
|
|
|
void ppc403_spu_rx_dma(UINT8 *data, int length)
|
|
{
|
|
|
|
}
|
|
|
|
void ppc403_install_spu_rx_dma_handler(PPC_DMA_HANDLER rx_dma_handler, UINT8 *buffer)
|
|
{
|
|
spu_rx_dma_handler = rx_dma_handler;
|
|
spu_rx_dma_ptr = buffer;
|
|
}
|
|
|
|
void ppc403_install_spu_tx_dma_handler(PPC_DMA_HANDLER tx_dma_handler, UINT8 *buffer)
|
|
{
|
|
spu_tx_dma_handler = tx_dma_handler;
|
|
spu_tx_dma_ptr = buffer;
|
|
}
|
|
|
|
/*********************************************************************************/
|
|
|
|
/* PPC 403 DMA */
|
|
|
|
static const int dma_transfer_width[4] = { 1, 2, 4, 16 };
|
|
|
|
void ppc403_install_dma_read_handler(int ch, PPC_DMA_HANDLER dma_handler, UINT8 *buffer)
|
|
{
|
|
dma_read_handler[ch] = dma_handler;
|
|
dma_read_ptr[ch] = buffer;
|
|
}
|
|
|
|
void ppc403_install_dma_write_handler(int ch, PPC_DMA_HANDLER dma_handler, UINT8 *buffer)
|
|
{
|
|
dma_write_handler[ch] = dma_handler;
|
|
dma_write_ptr[ch] = buffer;
|
|
}
|
|
|
|
static void ppc403_dma_exec(int ch)
|
|
{
|
|
int i;
|
|
int dai, sai, width;
|
|
|
|
/* Is the DMA channel enabled ? */
|
|
if( ppc.dma[ch].cr & DMA_CE )
|
|
{
|
|
/* transfer width */
|
|
width = dma_transfer_width[(ppc.dma[ch].cr >> 26) & 0x3];
|
|
|
|
if( ppc.dma[ch].cr & DMA_DAI )
|
|
dai = width;
|
|
else
|
|
dai = 0; /* DA not incremented */
|
|
|
|
if( ppc.dma[ch].cr & DMA_SAI )
|
|
sai = width;
|
|
else
|
|
sai = 0; /* SA not incremented */
|
|
|
|
|
|
/* transfer mode */
|
|
switch( (ppc.dma[ch].cr >> 21) & 0x3 )
|
|
{
|
|
case 0: /* buffered DMA */
|
|
if( ppc.dma[ch].cr & DMA_TD ) /* peripheral to mem */
|
|
{
|
|
// nothing to do for now */
|
|
}
|
|
else /* mem to peripheral */
|
|
{
|
|
|
|
/* check if the serial port is hooked to channel 2 or 3 */
|
|
if( (ch == 2 && ((ppc.spu.sptc >> 5) & 0x3) == 2) ||
|
|
(ch == 3 && ((ppc.spu.sptc >> 5) & 0x3) == 3) )
|
|
{
|
|
mame_printf_debug("ppc: dma_exec: DMA to serial port on channel %d (DA: %08X)\n", ch, ppc.dma[ch].da);
|
|
|
|
if (spu_tx_dma_handler)
|
|
{
|
|
int length = ppc.dma[ch].ct;
|
|
|
|
for( i=0; i < length; i++ ) {
|
|
spu_tx_dma_ptr[i] = memory_read_byte_32be(ppc.program, ppc.dma[ch].da++);
|
|
}
|
|
spu_tx_dma_handler(length);
|
|
}
|
|
|
|
#ifndef PPC_DRC
|
|
ppc403_set_irq_line(PPC_IRQ_SPU_TX, ASSERT_LINE);
|
|
#else
|
|
ppcdrc403_set_irq_line(PPC_IRQ_SPU_TX, ASSERT_LINE);
|
|
#endif
|
|
}
|
|
else {
|
|
fatalerror("ppc: dma_exec: buffered DMA to unknown peripheral ! (channel %d)", ch);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case 1: /* fly-by DMA */
|
|
fatalerror("ppc: dma_exec: fly-by DMA not implemented");
|
|
break;
|
|
|
|
case 2: /* software initiated mem-to-mem DMA */
|
|
//mame_printf_debug("ppc: DMA (%d, SW mem-to-mem): SA = %08X, DA = %08X, CT = %08X\n", ch, ppc.dma[ch].sa, ppc.dma[ch].da, ppc.dma[ch].ct);
|
|
|
|
switch(width)
|
|
{
|
|
case 1: /* Byte transfer */
|
|
for (i=0; i < ppc.dma[ch].ct; i++)
|
|
{
|
|
UINT8 b = READ8(ppc.dma[ch].sa);
|
|
WRITE8(ppc.dma[ch].da, b);
|
|
ppc.dma[ch].sa += sai;
|
|
ppc.dma[ch].da += dai;
|
|
}
|
|
break;
|
|
case 2: /* Word transfer */
|
|
for (i=0; i < ppc.dma[ch].ct; i++)
|
|
{
|
|
UINT16 w = READ16(ppc.dma[ch].sa);
|
|
WRITE16(ppc.dma[ch].da, w);
|
|
ppc.dma[ch].sa += sai;
|
|
ppc.dma[ch].da += dai;
|
|
}
|
|
break;
|
|
case 4: /* Double word transfer */
|
|
for (i=0; i < ppc.dma[ch].ct; i++)
|
|
{
|
|
UINT32 d = READ32(ppc.dma[ch].sa);
|
|
WRITE32(ppc.dma[ch].da, d);
|
|
ppc.dma[ch].sa += sai;
|
|
ppc.dma[ch].da += dai;
|
|
}
|
|
break;
|
|
case 16: /* 16-byte transfer */
|
|
for (i=0; i < ppc.dma[ch].ct; i++)
|
|
{
|
|
UINT32 d1 = READ32(ppc.dma[ch].sa+0);
|
|
UINT32 d2 = READ32(ppc.dma[ch].sa+4);
|
|
UINT32 d3 = READ32(ppc.dma[ch].sa+8);
|
|
UINT32 d4 = READ32(ppc.dma[ch].sa+12);
|
|
WRITE32(ppc.dma[ch].da+0, d1);
|
|
WRITE32(ppc.dma[ch].da+4, d2);
|
|
WRITE32(ppc.dma[ch].da+8, d3);
|
|
WRITE32(ppc.dma[ch].da+12, d4);
|
|
ppc.dma[ch].sa += 16;
|
|
ppc.dma[ch].da += 16;
|
|
}
|
|
break;
|
|
default:
|
|
fatalerror("dma: dma_exec: SW mem-to-mem DMA, width = %d", width);
|
|
}
|
|
break;
|
|
|
|
case 3: /* hardware initiated mem-to-mem DMA */
|
|
fatalerror("ppc: dma_exec: HW mem-to-mem DMA not implemented");
|
|
break;
|
|
}
|
|
|
|
ppc.dmasr |= (1 << (27 - ch));
|
|
|
|
/* DEBUG: check for not yet supported features */
|
|
if( (ppc.dma[ch].cr & DMA_TCE) == 0 )
|
|
fatalerror("ppc: dma_exec: DMA_TCE == 0");
|
|
|
|
if( ppc.dma[ch].cr & DMA_CH )
|
|
fatalerror("ppc: dma_exec: DMA chaining not implemented");
|
|
|
|
/* generate interrupts */
|
|
if( ppc.dma[ch].cr & DMA_CIE )
|
|
ppc403_dma_set_irq_line( ch, PULSE_LINE );
|
|
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************/
|
|
|
|
static UINT8 ppc403_read8(const address_space *space, UINT32 a)
|
|
{
|
|
if(a >= 0x40000000 && a <= 0x4000000f) /* Serial Port */
|
|
return ppc403_spu_r(a);
|
|
return memory_read_byte_32be(space, a);
|
|
}
|
|
|
|
#define ppc403_read16 memory_read_word_32be
|
|
#define ppc403_read32 memory_read_dword_32be
|
|
|
|
static void ppc403_write8(const address_space *space, UINT32 a, UINT8 d)
|
|
{
|
|
if( a >= 0x40000000 && a <= 0x4000000f ) /* Serial Port */
|
|
{
|
|
ppc403_spu_w(a, d);
|
|
return;
|
|
}
|
|
memory_write_byte_32be(space, a, d);
|
|
}
|
|
|
|
#define ppc403_write16 memory_write_word_32be
|
|
#define ppc403_write32 memory_write_dword_32be
|
|
|
|
static UINT16 ppc403_read16_unaligned(const address_space *space, UINT32 a)
|
|
{
|
|
fatalerror("ppc: Unaligned read16 %08X at %08X", a, ppc.pc);
|
|
return 0;
|
|
}
|
|
|
|
static UINT32 ppc403_read32_unaligned(const address_space *space, UINT32 a)
|
|
{
|
|
fatalerror("ppc: Unaligned read32 %08X at %08X", a, ppc.pc);
|
|
return 0;
|
|
}
|
|
|
|
static void ppc403_write16_unaligned(const address_space *space, UINT32 a, UINT16 d)
|
|
{
|
|
fatalerror("ppc: Unaligned write16 %08X, %04X at %08X", a, d, ppc.pc);
|
|
}
|
|
|
|
static void ppc403_write32_unaligned(const address_space *space, UINT32 a, UINT32 d)
|
|
{
|
|
fatalerror("ppc: Unaligned write32 %08X, %08X at %08X", a, d, ppc.pc);
|
|
}
|
|
|