#pragma once
#include "../common/Port.h"
#include "Sound.h"
extern const uint32_t objTilesAddress[3];
extern bool stopState;
extern bool holdState;
extern int cpuNextEvent;
extern bool cpuDmaHack;
extern uint32_t cpuDmaLast;
extern bool timer0On;
extern int timer0Ticks;
extern int timer0ClockReload;
extern bool timer1On;
extern int timer1Ticks;
extern int timer1ClockReload;
extern bool timer2On;
extern int timer2Ticks;
extern int timer2ClockReload;
extern bool timer3On;
extern int timer3Ticks;
extern int timer3ClockReload;
extern int cpuTotalTicks;
inline uint8_t CPUReadByteQuick(uint32_t addr) { return map[addr >> 24].address[addr & map[addr >> 24].mask]; }
inline uint16_t CPUReadHalfWordQuick(uint32_t addr) { return READ16LE(&map[addr >> 24].address[addr & map[addr >> 24].mask]); }
inline uint32_t CPUReadMemoryQuick(uint32_t addr) { return READ32LE(&map[addr >> 24].address[addr & map[addr >> 24].mask]); }
inline uint32_t CPUReadMemory(uint32_t address)
{
uint32_t value;
uint32_t oldAddress = address;
if (address & 3)
address &= ~0x03;
switch (address >> 24)
{
case 0:
if (reg[15].I >> 24)
{
if (address < 0x4000)
value = READ32LE(&biosProtected[0]);
else
goto unreadable;
}
else
value = READ32LE(&bios[address & 0x3FFC]);
break;
case 2:
value = READ32LE(&workRAM[address & 0x3FFFC]);
break;
case 3:
value = READ32LE(&internalRAM[address & 0x7ffC]);
break;
case 4:
if (address < 0x4000400 && ioReadable[address & 0x3fc])
{
if (ioReadable[(address & 0x3fc) + 2])
value = READ32LE(&ioMem[address & 0x3fC]);
else
value = READ16LE(&ioMem[address & 0x3fc]);
}
else
goto unreadable;
break;
case 5:
value = READ32LE(&paletteRAM[address & 0x3fC]);
break;
case 6:
address &= 0x1fffc;
if ((DISPCNT & 7) > 2 && (address & 0x1C000) == 0x18000)
{
value = 0;
break;
}
if ((address & 0x18000) == 0x18000)
address &= 0x17fff;
value = READ32LE(&vram[address]);
break;
case 7:
value = READ32LE(&oam[address & 0x3FC]);
break;
case 8:
case 9:
case 10:
case 11:
case 12:
value = READ32LE(&rom[address & 0x1FFFFFC]);
break;
case 13:
case 14:
case 15:
value = 0;
break;
// default
default:
unreadable:
if (cpuDmaHack)
value = cpuDmaLast;
else
{
if (armState)
return CPUReadMemoryQuick(reg[15].I);
else
return CPUReadHalfWordQuick(reg[15].I) | CPUReadHalfWordQuick(reg[15].I) << 16;
}
}
if (oldAddress & 3)
{
int shift = (oldAddress & 3) << 3;
value = (value >> shift) | (value << (32 - shift));
}
return value;
}
inline uint32_t CPUReadHalfWord(uint32_t address)
{
uint32_t value;
uint32_t oldAddress = address;
if (address & 1)
address &= ~0x01;
switch (address >> 24)
{
case 0:
if (reg[15].I >> 24)
{
if (address < 0x4000)
value = READ16LE(&biosProtected[address & 2]);
else
goto unreadable;
}
else
value = READ16LE(&bios[address & 0x3FFE]);
break;
case 2:
value = READ16LE(&workRAM[address & 0x3FFFE]);
break;
case 3:
value = READ16LE(&internalRAM[address & 0x7ffe]);
break;
case 4:
if (address < 0x4000400 && ioReadable[address & 0x3fe])
{
value = READ16LE(&ioMem[address & 0x3fe]);
if ((address & 0x3fe) > 0xFF && (address & 0x3fe) < 0x10E)
{
if ((address & 0x3fe) == 0x100 && timer0On)
value = 0xFFFF - ((timer0Ticks - cpuTotalTicks) >> timer0ClockReload);
else if ((address & 0x3fe) == 0x104 && timer1On && !(TM1CNT & 4))
value = 0xFFFF - ((timer1Ticks - cpuTotalTicks) >> timer1ClockReload);
else if ((address & 0x3fe) == 0x108 && timer2On && !(TM2CNT & 4))
value = 0xFFFF - ((timer2Ticks - cpuTotalTicks) >> timer2ClockReload);
else if ((address & 0x3fe) == 0x10C && timer3On && !(TM3CNT & 4))
value = 0xFFFF - ((timer3Ticks - cpuTotalTicks) >> timer3ClockReload);
}
}
else if (address < 0x4000400 && ioReadable[address & 0x3fc])
value = 0;
else
goto unreadable;
break;
case 5:
value = READ16LE(&paletteRAM[address & 0x3fe]);
break;
case 6:
address &= 0x1fffe;
if ((DISPCNT & 7) > 2 && (address & 0x1C000) == 0x18000)
{
value = 0;
break;
}
if ((address & 0x18000) == 0x18000)
address &= 0x17fff;
value = READ16LE(&vram[address]);
break;
case 7:
value = READ16LE(&oam[address & 0x3fe]);
break;
case 8:
case 9:
case 10:
case 11:
case 12:
if (address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)
value = 0;
else
value = READ16LE(&rom[address & 0x1FFFFFE]);
break;
case 13:
case 14:
case 15:
value = 0;
break;
// default
default:
unreadable:
if (cpuDmaHack)
value = cpuDmaLast & 0xFFFF;
else
{
if (armState)
value = CPUReadHalfWordQuick(reg[15].I + (address & 2));
else
value = CPUReadHalfWordQuick(reg[15].I);
}
return value;
}
if (oldAddress & 1)
value = (value >> 8) | (value << 24);
return value;
}
inline int16_t CPUReadHalfWordSigned(uint32_t address)
{
return static_cast<int16_t>(CPUReadHalfWord(address));
}
inline uint8_t CPUReadByte(uint32_t address)
{
switch (address >> 24)
{
case 0:
if (reg[15].I >> 24)
{
if (address < 0x4000)
return biosProtected[address & 3];
else
break;
}
return bios[address & 0x3FFF];
case 2:
return workRAM[address & 0x3FFFF];
case 3:
return internalRAM[address & 0x7fff];
case 4:
if (address < 0x4000400 && ioReadable[address & 0x3ff])
return ioMem[address & 0x3ff];
else
break;
case 5:
return paletteRAM[address & 0x3ff];
case 6:
address &= 0x1ffff;
if ((DISPCNT & 7) > 2 && (address & 0x1C000) == 0x18000)
return 0;
if ((address & 0x18000) == 0x18000)
address &= 0x17fff;
return vram[address];
case 7:
return oam[address & 0x3ff];
case 8:
case 9:
case 10:
case 11:
case 12:
return rom[address & 0x1FFFFFF];
case 13:
case 14:
case 15:
return 0;
}
if (cpuDmaHack)
return cpuDmaLast & 0xFF;
else
{
if (armState)
return CPUReadByteQuick(reg[15].I + (address & 3));
else
return CPUReadByteQuick(reg[15].I + (address & 1));
}
}
inline void CPUWriteMemory(uint32_t address, uint32_t value)
{
address &= 0xFFFFFFFC;
switch (address >> 24)
{
case 0x02:
WRITE32LE(&workRAM[address & 0x3FFFC], value);
break;
case 0x03:
WRITE32LE(&internalRAM[address & 0x7ffC], value);
break;
case 0x04:
if (address < 0x4000400)
{
CPUUpdateRegister(address & 0x3FC, value & 0xFFFF);
CPUUpdateRegister((address & 0x3FC) + 2, value >> 16);
}
break;
case 0x05:
WRITE32LE(&paletteRAM[address & 0x3FC], value);
break;
case 0x06:
address &= 0x1fffc;
if ((DISPCNT & 7) > 2 && (address & 0x1C000) == 0x18000)
return;
if ((address & 0x18000) == 0x18000)
address &= 0x17fff;
WRITE32LE(&vram[address], value);
break;
case 0x07:
WRITE32LE(&oam[address & 0x3fc], value);
}
}
inline void CPUWriteHalfWord(uint32_t address, uint16_t value)
{
address &= 0xFFFFFFFE;
switch (address >> 24)
{
case 2:
WRITE16LE(&workRAM[address & 0x3FFFE], value);
break;
case 3:
WRITE16LE(&internalRAM[address & 0x7ffe], value);
break;
case 4:
if (address < 0x4000400)
CPUUpdateRegister(address & 0x3fe, value);
break;
case 5:
WRITE16LE(&paletteRAM[address & 0x3fe], value);
break;
case 6:
address &= 0x1fffe;
if ((DISPCNT & 7) > 2 && (address & 0x1C000) == 0x18000)
return;
if ((address & 0x18000) == 0x18000)
address &= 0x17fff;
WRITE16LE(&vram[address], value);
break;
case 7:
WRITE16LE(&oam[address & 0x3fe], value);
}
}
inline void CPUWriteByte(uint32_t address, uint8_t b)
{
switch (address >> 24)
{
case 2:
workRAM[address & 0x3FFFF] = b;
break;
case 3:
internalRAM[address & 0x7fff] = b;
break;
case 4:
if (address < 0x4000400)
{
switch (address & 0x3FF)
{
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x68:
case 0x69:
case 0x6c:
case 0x6d:
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x78:
case 0x79:
case 0x7c:
case 0x7d:
case 0x80:
case 0x81:
case 0x84:
case 0x85:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
soundEvent(address & 0xFF, b);
break;
case 0x301: // HALTCNT, undocumented
if (b == 0x80)
stopState = true;
holdState = true;
cpuNextEvent = cpuTotalTicks;
break;
default: // every other register
uint32_t lowerBits = address & 0x3fe;
if (address & 1)
CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8));
else
CPUUpdateRegister(lowerBits, (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b);
}
}
break;
case 5:
// no need to switch
*reinterpret_cast<uint16_t *>(&paletteRAM[address & 0x3FE]) = (b << 8) | b;
break;
case 6:
address &= 0x1fffe;
if ((DISPCNT & 7) > 2 && (address & 0x1C000) == 0x18000)
return;
if ((address & 0x18000) == 0x18000)
address &= 0x17fff;
// no need to switch
// byte writes to OBJ VRAM are ignored
if (address < objTilesAddress[((DISPCNT & 7) + 1) >> 2])
*reinterpret_cast<uint16_t *>(&vram[address]) = (b << 8) | b;
break;
case 7:
// no need to switch
// byte writes to OAM are ignored
//*reinterpret_cast<uint16_t *>(&oam[address & 0x3FE]) = (b << 8) | b;
break;
}
}