#include "ameteor/io.hpp"
#include "ameteor/dma.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include <cstring>
#include "debug.hpp"
#define W8(add, val) \
m_iomem[(add) & 0xFFF] = (val)
#define W16(add, val) \
*(uint16_t*)(m_iomem + ((add) & 0xFFF)) = (val)
#define W32(add, val) \
*(uint32_t*)(m_iomem + ((add) & 0xFFF)) = (val)
namespace AMeteor
{
Io::Io ()
{
m_iomem = new uint8_t[IO_SIZE];
Reset ();
}
Io::~Io ()
{
delete [] m_iomem;
}
void Io::Reset ()
{
std::memset(m_iomem, 0, IO_SIZE);
W16(SOUNDBIAS, 0x0200);
W16(KEYINPUT, 0x03FF);
W8(HALTCNT, 0xFF);
W16(DISPSTAT, 0x0004);
W16(BG2PA, 0x0100);
W16(BG2PD, 0x0100);
W16(BG3PA, 0x0100);
W16(BG3PD, 0x0100);
W16(RCNT, 0x8000);
}
void Io::ClearSio ()
{
W16(RCNT, 0x8000);
}
void Io::ClearSound ()
{
}
void Io::ClearOthers ()
{
for (uint8_t i = 0x0; i < 0x56; i += 2)
Write16(i, 0x0000);
for (uint8_t i = 0xB0; i < 0xE0; i += 4)
Write32(i, 0x0000);
W8(HALTCNT, 0xFF);
W16(IE, 0x0000);
W16(IF, 0x0000);
W16(IME, 0x0000);
Write16(WAITCNT, 0x0000);
W16(BG2PA, 0x0100);
W16(BG2PD, 0x0100);
W16(BG3PA, 0x0100);
W16(BG3PD, 0x0100);
}
uint8_t Io::Read8 (uint32_t add)
{
if ((add & 0xFFE) == KEYINPUT)
keyupdate_bizhawk();
if ((add & 0xFF0) == 0x100)
switch (add & 0xF)
{
case 0x0:
case 0x4:
case 0x8:
case 0xC:
met_abort("Misaligned reading of timers");
}
return m_iomem[add & 0xFFF];
}
uint16_t Io::Read16 (uint32_t add)
{
if ((add & 0xFFE) == KEYINPUT)
keyupdate_bizhawk();
if ((add & 0xFF0) == 0x100)
switch (add & 0xF)
{
case 0x0: return TIMER0.GetCount();
case 0x4: return TIMER1.GetCount();
case 0x8: return TIMER2.GetCount();
case 0xC: return TIMER3.GetCount();
}
return *(uint16_t*)(m_iomem + (add & 0xFFF));
}
uint32_t Io::Read32 (uint32_t add)
{
if ((add & 0xFFC) == KEYINPUT)
keyupdate_bizhawk();
if ((add & 0xFF0) == 0x100)
switch (add & 0xF)
{
case 0x0: return TIMER0.GetCount();
case 0x4: return TIMER1.GetCount();
case 0x8: return TIMER2.GetCount();
case 0xC: return TIMER3.GetCount();
}
return *(uint32_t*)(m_iomem + (add & 0xFFF));
}
void Io::Write8 (uint32_t add, uint8_t val)
{
switch (add & 0xFFF)
{
case NR10+1:
case NR52+1:
case NR52+2:
case NR52+3:
break;
case NR10:
case NR11:
case NR13:
case NR21:
case NR23:
case NR41:
case NR43:
case NR50:
case NR51:
case SOUNDCNT_H:
W8(add, val);
break;
case NR12:
W8(add, val);
if (!(val & (0xF << 4)))
SOUND.ResetSound1Envelope();
break;
case NR14:
W8(add, val & 0xC7);
if (val & (0x1 << 7))
SOUND.ResetSound1();
break;
case NR22:
W8(add, val);
if (!(val & (0xF << 4)))
SOUND.ResetSound2Envelope();
break;
case NR24:
W8(add, val & 0xC7);
if (val & (0x1 << 7))
SOUND.ResetSound2();
break;
case NR42:
W8(add, val);
if (!(val & (0xF << 4)))
SOUND.ResetSound4Envelope();
break;
case NR44:
W8(add, val & 0xC7);
if (val & (0x1 << 7))
SOUND.ResetSound4();
break;
case SOUNDCNT_H+1:
W8(add, val);
SOUND.UpdateCntH1(val);
break;
case NR52:
W8(add, val & 0x80);
break;
case POSTFLG:
if (val)
val &= 0xFE;
W8(add, val);
break;
case HALTCNT:
W8(add, val);
break;
default:
#if 1
add &= 0xFFF;
if (add % 2)
Write16(add & ~0x1, (val << 8) | m_iomem[add & ~0x1]);
else
Write16(add, (m_iomem[add | 0x1] << 8) | val);
#endif
break;
}
}
void Io::Write16 (uint32_t add, uint16_t val)
{
switch (add & 0xFFF)
{
case KEYINPUT:
case VCOUNT:
break;
case DMA0CNT_L:
case DMA1CNT_L:
case DMA2CNT_L:
case DMA3CNT_L:
DMA.SetReload(((add & 0xFFF) - DMA0CNT_L) / DMA_CHANSIZE, val);
break;
case KEYCNT:
W16(add, val & 0xC3FF);
break;
case IME:
W16(add, val & 0x0001);
CPU.CheckInterrupt();
break;
case IE:
W16(add, val & 0x3FFF);
CPU.CheckInterrupt();
break;
case IF:
*((uint16_t*)(m_iomem+IF)) ^= (val & (*((uint16_t*)(m_iomem+IF))));
CPU.CheckInterrupt();
break;
case BG0CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg0Cnt(val & 0xFFCF);
break;
case BG1CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg1Cnt(val & 0xFFCF);
break;
case BG2CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg2Cnt(val & 0xFFCF);
break;
case BG3CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg3Cnt(val & 0xFFCF);
break;
case DISPSTAT:
W16(add, (val & 0xFFF8) | (m_iomem[add & 0xFFF] & 0x07));
break;
case BG0HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg0XOff(val & 0x1FF);
break;
case BG0VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg0YOff(val & 0x1FF);
break;
case BG1HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg1XOff(val & 0x1FF);
break;
case BG1VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg1YOff(val & 0x1FF);
break;
case BG2HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg2XOff(val & 0x1FF);
break;
case BG2VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg2YOff(val & 0x1FF);
break;
case BG3HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg3XOff(val & 0x1FF);
break;
case BG3VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg3YOff(val & 0x1FF);
break;
case BG2X_H:
val &= 0x0FFF;
case BG2X_L:
W16(add, val);
LCD.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
break;
case BG2Y_H:
val &= 0x0FFF;
case BG2Y_L:
W16(add, val);
LCD.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
break;
case BG3X_H:
val &= 0x0FFF;
case BG3X_L:
W16(add, val);
LCD.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
break;
case BG3Y_H:
val &= 0x0FFF;
case BG3Y_L:
W16(add, val);
LCD.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
break;
case WIN0H:
case WIN1H:
case WIN0V:
case WIN1V:
case WININ:
case WINOUT:
W16(add, val);
break;
case BLDCNT:
W16(add, val);
break;
case MOSAIC:
W16(add, val);
break;
case DISPCNT:
W16(add, val);
LCD.UpdateDispCnt(val);
break;
case DMA0CNT_H:
case DMA1CNT_H:
case DMA2CNT_H:
case DMA3CNT_H:
W16(add, val & 0xFFE0);
DMA.UpdateCnt(((add & 0xFFF) - DMA0CNT_H) / DMA_CHANSIZE);
break;
case WAITCNT:
W16(add, val & 0xDFFF);
MEM.UpdateWaitStates (val & 0xDFFF);
break;
case SOUND1CNT_L:
case SOUND1CNT_H:
case SOUND1CNT_X:
case SOUND2CNT_L:
case SOUND2CNT_H:
case SOUND4CNT_L:
case SOUND4CNT_H:
case SOUNDCNT_L:
case SOUNDCNT_H:
case SOUNDCNT_X:
case POSTFLG:
Write8(add, val & 0xFF);
Write8(add + 1, val >> 8);
break;
case TM0CNT_L:
TIMER0.SetReload(val);
break;
case TM1CNT_L:
TIMER1.SetReload(val);
break;
case TM2CNT_L:
TIMER2.SetReload(val);
break;
case TM3CNT_L:
TIMER3.SetReload(val);
break;
case TM0CNT_H:
W16(add, val & 0x00C7);
TIMER0.Reload();
break;
case TM1CNT_H:
W16(add, val & 0x00C7);
TIMER1.Reload();
break;
case TM2CNT_H:
W16(add, val & 0x00C7);
TIMER2.Reload();
break;
case TM3CNT_H:
W16(add, val & 0x00C7);
TIMER3.Reload();
break;
default:
W16(add, val);
break;
}
}
void Io::Write32 (uint32_t add, uint32_t val)
{
switch (add & 0xFF)
{
case DMA1DAD:
case DMA0SAD:
case DMA1SAD:
case DMA2SAD:
case DMA3SAD:
case DMA0DAD:
case DMA2DAD:
case DMA3DAD:
W32(add, val);
break;
case BG0HOFS:
case BG1HOFS:
case BG2HOFS:
case BG3HOFS:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
case BG2X_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
break;
case BG2Y_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
break;
case BG3X_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
break;
case BG3Y_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
break;
case BG2PA:
case BG2PC:
case BG3PA:
case BG3PC:
case WIN0H:
case WIN0V:
case WININ:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
case DMA0CNT_L:
case DMA1CNT_L:
case DMA2CNT_L:
case DMA3CNT_L:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
case FIFO_A:
case FIFO_B:
break;
default:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
}
}
bool Io::SaveState (std::ostream& stream)
{
SS_WRITE_DATA(m_iomem, IO_SIZE);
return true;
}
bool Io::LoadState (std::istream& stream)
{
SS_READ_DATA(m_iomem, IO_SIZE);
return true;
}
}