in cores/snes/ppu.cpp [1359:1686]
void S9xSetCPU (uint8 Byte, uint16 Address)
{
if (Address < 0x4200)
{
switch (Address)
{
case 0x4016: // JOYSER0
S9xSetJoypadLatch(Byte & 1);
break;
case 0x4017: // JOYSER1
return;
default:
break;
}
}
else
if ((Address & 0xff80) == 0x4300)
{
if (CPU.InDMAorHDMA)
return;
int d = (Address >> 4) & 0x7;
switch (Address & 0xf)
{
case 0x0: // 0x43x0: DMAPx
DMA[d].ReverseTransfer = (Byte & 0x80) ? TRUE : FALSE;
DMA[d].HDMAIndirectAddressing = (Byte & 0x40) ? TRUE : FALSE;
DMA[d].UnusedBit43x0 = (Byte & 0x20) ? TRUE : FALSE;
DMA[d].AAddressDecrement = (Byte & 0x10) ? TRUE : FALSE;
DMA[d].AAddressFixed = (Byte & 0x08) ? TRUE : FALSE;
DMA[d].TransferMode = (Byte & 7);
return;
case 0x1: // 0x43x1: BBADx
DMA[d].BAddress = Byte;
return;
case 0x2: // 0x43x2: A1TxL
DMA[d].AAddress &= 0xff00;
DMA[d].AAddress |= Byte;
return;
case 0x3: // 0x43x3: A1TxH
DMA[d].AAddress &= 0xff;
DMA[d].AAddress |= Byte << 8;
return;
case 0x4: // 0x43x4: A1Bx
DMA[d].ABank = Byte;
HDMAMemPointers[d] = NULL;
return;
case 0x5: // 0x43x5: DASxL
DMA[d].DMACount_Or_HDMAIndirectAddress &= 0xff00;
DMA[d].DMACount_Or_HDMAIndirectAddress |= Byte;
HDMAMemPointers[d] = NULL;
return;
case 0x6: // 0x43x6: DASxH
DMA[d].DMACount_Or_HDMAIndirectAddress &= 0xff;
DMA[d].DMACount_Or_HDMAIndirectAddress |= Byte << 8;
HDMAMemPointers[d] = NULL;
return;
case 0x7: // 0x43x7: DASBx
DMA[d].IndirectBank = Byte;
HDMAMemPointers[d] = NULL;
return;
case 0x8: // 0x43x8: A2AxL
DMA[d].Address &= 0xff00;
DMA[d].Address |= Byte;
HDMAMemPointers[d] = NULL;
return;
case 0x9: // 0x43x9: A2AxH
DMA[d].Address &= 0xff;
DMA[d].Address |= Byte << 8;
HDMAMemPointers[d] = NULL;
return;
case 0xa: // 0x43xa: NLTRx
if (Byte & 0x7f)
{
DMA[d].LineCount = Byte & 0x7f;
DMA[d].Repeat = !(Byte & 0x80);
}
else
{
DMA[d].LineCount = 128;
DMA[d].Repeat = !!(Byte & 0x80);
}
return;
case 0xb: // 0x43xb: ????x
case 0xf: // 0x43xf: mirror of 0x43xb
DMA[d].UnknownByte = Byte;
return;
default:
break;
}
}
else
{
uint16 pos;
switch (Address)
{
case 0x4200: // NMITIMEN
if (Byte & 0x20)
{
PPU.VTimerEnabled = TRUE;
#ifdef DEBUGGER
missing.virq = 1;
missing.virq_pos = PPU.IRQVBeamPos;
#endif
}
else
PPU.VTimerEnabled = FALSE;
if (Byte & 0x10)
{
PPU.HTimerEnabled = TRUE;
#ifdef DEBUGGER
missing.hirq = 1;
missing.hirq_pos = PPU.IRQHBeamPos;
#endif
}
else
PPU.HTimerEnabled = FALSE;
if (CPU.IRQLine && !PPU.HTimerEnabled && PPU.VTimerEnabled)
CPU.IRQTransition = TRUE;
if (!PPU.HTimerEnabled && !PPU.VTimerEnabled)
{
CPU.IRQLine = FALSE;
CPU.IRQTransition = FALSE;
}
// NMI can trigger immediately during VBlank as long as NMI_read ($4210) wasn't cleard.
if ((Byte & 0x80) && !(Memory.FillRAM[0x4200] & 0x80) &&
(CPU.V_Counter >= PPU.ScreenHeight + FIRST_VISIBLE_LINE) && (Memory.FillRAM[0x4210] & 0x80))
{
// FIXME: triggered at HC+=6, checked just before the final CPU cycle,
// then, when to call S9xOpcode_NMI()?
CPU.NMILine = TRUE;
Timings.NMITriggerPos = CPU.Cycles + 6 + 6;
}
#ifdef DEBUGGER
S9xTraceFormattedMessage("--- IRQ Timer Enable HTimer:%d Pos:%04d VTimer:%d Pos:%03d",
PPU.HTimerEnabled, PPU.HTimerPosition, PPU.VTimerEnabled, PPU.VTimerPosition);
#endif
break;
case 0x4201: // WRIO
if ((Byte & 0x80) == 0 && (Memory.FillRAM[0x4213] & 0x80) == 0x80)
S9xLatchCounters(1);
else
S9xTryGunLatch((Byte & 0x80) ? true : false);
Memory.FillRAM[0x4201] = Memory.FillRAM[0x4213] = Byte;
break;
case 0x4202: // WRMPYA
break;
case 0x4203: // WRMPYB
{
uint32 res = Memory.FillRAM[0x4202] * Byte;
// FIXME: The update occurs 8 machine cycles after $4203 is set.
Memory.FillRAM[0x4216] = (uint8) res;
Memory.FillRAM[0x4217] = (uint8) (res >> 8);
break;
}
case 0x4204: // WRDIVL
case 0x4205: // WRDIVH
break;
case 0x4206: // WRDIVB
{
uint16 a = Memory.FillRAM[0x4204] + (Memory.FillRAM[0x4205] << 8);
uint16 div = Byte ? a / Byte : 0xffff;
uint16 rem = Byte ? a % Byte : a;
// FIXME: The update occurs 16 machine cycles after $4206 is set.
Memory.FillRAM[0x4214] = (uint8) div;
Memory.FillRAM[0x4215] = div >> 8;
Memory.FillRAM[0x4216] = (uint8) rem;
Memory.FillRAM[0x4217] = rem >> 8;
break;
}
case 0x4207: // HTIMEL
pos = PPU.IRQHBeamPos;
PPU.IRQHBeamPos = (PPU.IRQHBeamPos & 0xff00) | Byte;
if (PPU.IRQHBeamPos != pos)
S9xUpdateHVTimerPosition();
#ifdef DEBUGGER
missing.hirq_pos = PPU.IRQHBeamPos;
#endif
break;
case 0x4208: // HTIMEH
pos = PPU.IRQHBeamPos;
PPU.IRQHBeamPos = (PPU.IRQHBeamPos & 0xff) | ((Byte & 1) << 8);
if (PPU.IRQHBeamPos != pos)
S9xUpdateHVTimerPosition();
#ifdef DEBUGGER
missing.hirq_pos = PPU.IRQHBeamPos;
#endif
break;
case 0x4209: // VTIMEL
pos = PPU.IRQVBeamPos;
PPU.IRQVBeamPos = (PPU.IRQVBeamPos & 0xff00) | Byte;
if (PPU.IRQVBeamPos != pos)
S9xUpdateHVTimerPosition();
#ifdef DEBUGGER
missing.virq_pos = PPU.IRQVBeamPos;
#endif
break;
case 0x420a: // VTIMEH
pos = PPU.IRQVBeamPos;
PPU.IRQVBeamPos = (PPU.IRQVBeamPos & 0xff) | ((Byte & 1) << 8);
if (PPU.IRQVBeamPos != pos)
S9xUpdateHVTimerPosition();
#ifdef DEBUGGER
missing.virq_pos = PPU.IRQVBeamPos;
#endif
break;
case 0x420b: // MDMAEN
if (CPU.InDMAorHDMA)
return;
// XXX: Not quite right...
if (Byte) {
CPU.PrevCycles = CPU.Cycles;
CPU.Cycles += Timings.DMACPUSync;
S9xCheckInterrupts();
}
if (Byte & 0x01)
S9xDoDMA(0);
if (Byte & 0x02)
S9xDoDMA(1);
if (Byte & 0x04)
S9xDoDMA(2);
if (Byte & 0x08)
S9xDoDMA(3);
if (Byte & 0x10)
S9xDoDMA(4);
if (Byte & 0x20)
S9xDoDMA(5);
if (Byte & 0x40)
S9xDoDMA(6);
if (Byte & 0x80)
S9xDoDMA(7);
#ifdef DEBUGGER
missing.dma_this_frame = Byte;
missing.dma_channels = Byte;
#endif
break;
case 0x420c: // HDMAEN
if (CPU.InDMAorHDMA)
return;
Memory.FillRAM[0x420c] = Byte;
// Yoshi's Island, Genjyu Ryodan, Mortal Kombat, Tales of Phantasia
PPU.HDMA = Byte & ~PPU.HDMAEnded;
#ifdef DEBUGGER
missing.hdma_this_frame |= Byte;
missing.hdma_channels |= Byte;
#endif
break;
case 0x420d: // MEMSEL
if ((Byte & 1) != (Memory.FillRAM[0x420d] & 1))
{
if (Byte & 1)
{
CPU.FastROMSpeed = ONE_CYCLE;
#ifdef DEBUGGER
missing.fast_rom = 1;
#endif
}
else
CPU.FastROMSpeed = SLOW_ONE_CYCLE;
}
break;
case 0x4210: // RDNMI
case 0x4211: // TIMEUP
case 0x4212: // HVBJOY
case 0x4213: // RDIO
case 0x4214: // RDDIVL
case 0x4215: // RDDIVH
case 0x4216: // RDMPYL
case 0x4217: // RDMPYH
case 0x4218: // JOY1L
case 0x4219: // JOY1H
case 0x421a: // JOY2L
case 0x421b: // JOY2H
case 0x421c: // JOY3L
case 0x421d: // JOY3H
case 0x421e: // JOY4L
case 0x421f: // JOY4H
return;
default:
if (Settings.SPC7110 && Address >= 0x4800)
S9xSetSPC7110(Byte, Address);
else
if (Settings.SDD1 && Address >= 0x4804 && Address <= 0x4807)
S9xSetSDD1MemoryMap(Address - 0x4804, Byte & 7);
break;
}
}
Memory.FillRAM[Address] = Byte;
}