#include namespace GameBoyAdvance { #include "mmio.cpp" #include "serialization.cpp" Bus bus; struct UnmappedMemory : Memory { uint32 read(uint32 addr, uint32 size) { return 0u; } void write(uint32 addr, uint32 size, uint32 word) {} }; static UnmappedMemory unmappedMemory; uint32 Bus::mirror(uint32 addr, uint32 size) { uint32 base = 0; if(size) { uint32 mask = 1 << 27; //28-bit bus while(addr >= size) { while(!(addr & mask)) mask >>= 1; addr -= mask; if(size > mask) { size -= mask; base += mask; } mask >>= 1; } base += addr; } return base; } uint32 Bus::speed(uint32 addr, uint32 size) { if(addr & 0x08000000) { static unsigned timing[] = {5, 4, 3, 9}; unsigned n = cpu.regs.wait.control.nwait[addr >> 25 & 3]; unsigned s = cpu.regs.wait.control.swait[addr >> 25 & 3]; n = timing[n]; switch(addr >> 25 & 3) { case 0: s = s ? 3 : 2; break; case 1: s = s ? 5 : 2; break; case 2: s = s ? 9 : 2; break; case 3: s = n; break; } bool sequential = cpu.sequential(); if((addr & 0xffff << 1) == 0) sequential = false; //N cycle on 16-bit ROM crossing page boundary (RAM S==N) if(idleflag) sequential = false; //LDR/LDM interrupts instruction fetches if(sequential) return s << (size == Word); //16-bit bus requires two transfers for words if(size == Word) n += s; return n; } switch(addr >> 24 & 7) { case 0: return 1; case 1: return 1; case 2: return (1 + 15 - cpu.regs.memory.control.ewramwait) << (size == Word); case 3: return 1; case 4: return 1; case 5: return 1 << (size == Word); case 6: return 1 << (size == Word); case 7: return 1; } } void Bus::idle(uint32 addr) { if(addr & 0x08000000) idleflag = true; } uint32 Bus::read(uint32 addr, uint32 size) { idleflag = false; if(addr & 0x08000000) return cartridge.read(addr, size); switch(addr >> 24 & 7) { case 0: return bios.read(addr, size); case 1: return bios.read(addr, size); case 2: return cpu.ewram_read(addr, size); case 3: return cpu.iwram_read(addr, size); case 4: if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size); if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size); return cpu.pipeline.fetch.instruction; case 5: return ppu.pram_read(addr, size); case 6: return ppu.vram_read(addr, size); case 7: return ppu.oam_read(addr, size); } } void Bus::write(uint32 addr, uint32 size, uint32 word) { idleflag = false; if(addr & 0x08000000) return cartridge.write(addr, size, word); switch(addr >> 24 & 7) { case 0: return; case 1: return; case 2: return cpu.ewram_write(addr, size, word); case 3: return cpu.iwram_write(addr, size, word); case 4: if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word); if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word); return; case 5: return ppu.pram_write(addr, size, word); case 6: return ppu.vram_write(addr, size, word); case 7: return ppu.oam_write(addr, size, word); } } void Bus::power() { for(unsigned n = 0; n < 0x400; n++) mmio[n] = &unmappedMemory; idleflag = false; } }