bsnes/higan/sfc/ppu/mmio.cpp

726 lines
17 KiB
C++

auto PPU::read(uint24 addr, uint8 data) -> uint8 {
cpu.synchronizePPU();
switch((uint16)addr) {
case 0x2104: case 0x2105: case 0x2106: case 0x2108:
case 0x2109: case 0x210a: case 0x2114: case 0x2115:
case 0x2116: case 0x2118: case 0x2119: case 0x211a:
case 0x2124: case 0x2125: case 0x2126: case 0x2128:
case 0x2129: case 0x212a: {
return ppu1.mdr;
}
//MPYL
case 0x2134: {
uint result = (int16)r.m7a * (int8)(r.m7b >> 8);
ppu1.mdr = (result >> 0);
return ppu1.mdr;
}
//MPYM
case 0x2135: {
uint result = (int16)r.m7a * (int8)(r.m7b >> 8);
ppu1.mdr = (result >> 8);
return ppu1.mdr;
}
//MPYH
case 0x2136: {
uint result = (int16)r.m7a * (int8)(r.m7b >> 8);
ppu1.mdr = (result >> 16);
return ppu1.mdr;
}
//SLHV
case 0x2137: {
if(cpu.pio() & 0x80) latchCounters();
return cpu.r.mdr;
}
//OAMDATAREAD
case 0x2138: {
uint10 address = r.oamAddress++;
if(!r.displayDisable && vcounter() < vdisp()) address = latch.oamAddress;
if(address & 0x0200) address &= 0x021f;
ppu1.mdr = oamRead(address);
obj.setFirstSprite();
return ppu1.mdr;
}
//VMDATALREAD
case 0x2139: {
uint16 address = getVramAddress() + 0;
ppu1.mdr = latch.vram >> 0;
if(r.vramIncrementMode == 0) {
address.bit(0) = 0;
latch.vram.byte(0) = vramRead(address + 0);
latch.vram.byte(1) = vramRead(address + 1);
r.vramAddress += r.vramIncrementSize;
}
return ppu1.mdr;
}
//VMDATAHREAD
case 0x213a: {
uint16 address = getVramAddress() + 1;
ppu1.mdr = latch.vram >> 8;
if(r.vramIncrementMode == 1) {
address.bit(0) = 0;
latch.vram.byte(0) = vramRead(address + 0);
latch.vram.byte(1) = vramRead(address + 1);
r.vramAddress += r.vramIncrementSize;
}
return ppu1.mdr;
}
//CGDATAREAD
case 0x213b: {
bool l = r.cgramAddress & 1;
uint9 address = r.cgramAddress++;
if(!r.displayDisable
&& vcounter() > 0 && vcounter() < vdisp()
&& hcounter() >= 88 && hcounter() < 1096
) address = latch.cgramAddress;
if(l == 0) {
ppu2.mdr = cgramRead(address);
} else {
ppu2.mdr &= 0x80;
ppu2.mdr |= cgramRead(address);
}
return ppu2.mdr;
}
//OPHCT
case 0x213c: {
if(latch.hcounter == 0) {
ppu2.mdr = (r.hcounter >> 0);
} else {
ppu2.mdr &= 0xfe;
ppu2.mdr |= (r.hcounter >> 8) & 1;
}
latch.hcounter ^= 1;
return ppu2.mdr;
}
//OPVCT
case 0x213d: {
if(latch.vcounter == 0) {
ppu2.mdr = (r.vcounter >> 0);
} else {
ppu2.mdr &= 0xfe;
ppu2.mdr |= (r.vcounter >> 8) & 1;
}
latch.vcounter ^= 1;
return ppu2.mdr;
}
//STAT77
case 0x213e: {
ppu1.mdr &= 0x10;
ppu1.mdr |= obj.r.timeOver << 7;
ppu1.mdr |= obj.r.rangeOver << 6;
ppu1.mdr |= ppu1.version & 0x0f;
return ppu1.mdr;
}
//STAT78
case 0x213f: {
latch.hcounter = 0;
latch.vcounter = 0;
ppu2.mdr &= 0x20;
ppu2.mdr |= field() << 7;
if((cpu.pio() & 0x80) == 0) {
ppu2.mdr |= 0x40;
} else if(latch.counters) {
ppu2.mdr |= 0x40;
latch.counters = false;
}
ppu2.mdr |= (system.region() == System::Region::NTSC ? 0 : 1) << 4;
ppu2.mdr |= ppu2.version & 0x0f;
return ppu2.mdr;
}
}
return data;
}
auto PPU::write(uint24 addr, uint8 data) -> void {
cpu.synchronizePPU();
switch((uint16)addr) {
//INIDISP
case 0x2100: {
if(r.displayDisable && vcounter() == vdisp()) obj.addressReset();
r.displayBrightness = data.bits(0,3);
r.displayDisable = data.bit (7);
return;
}
//OBSEL
case 0x2101: {
obj.r.tiledataAddress = data.bits(0,1) << 14;
obj.r.nameSelect = data.bits(3,4);
obj.r.baseSize = data.bits(5,7);
return;
}
//OAMADDL
case 0x2102: {
r.oamBaseAddress = (r.oamBaseAddress & 0x0200) | (data << 1);
obj.addressReset();
return;
}
//OAMADDH
case 0x2103: {
r.oamPriority = data & 0x80;
r.oamBaseAddress = ((data & 0x01) << 9) | (r.oamBaseAddress & 0x01fe);
obj.addressReset();
return;
}
//OAMDATA
case 0x2104: {
bool l = r.oamAddress & 1;
uint10 address = r.oamAddress++;
if(!r.displayDisable && vcounter() < vdisp()) address = latch.oamAddress;
if(address & 0x0200) address &= 0x021f;
if(l == 0) latch.oam = data;
if(address & 0x0200) {
oamWrite(address, data);
} else if(l == 1) {
oamWrite((address & ~1) + 0, latch.oam);
oamWrite((address & ~1) + 1, data);
}
obj.setFirstSprite();
return;
}
//BGMODE
case 0x2105: {
r.bgMode = data.bits(0,2);
r.bgPriority = data.bit (3);
bg1.r.tileSize = data.bit (4);
bg2.r.tileSize = data.bit (5);
bg3.r.tileSize = data.bit (6);
bg4.r.tileSize = data.bit (7);
updateVideoMode();
return;
}
//MOSAIC
case 0x2106: {
uint mosaicSize = data.bits(4,7);
bg1.r.mosaic = data.bit(0) ? mosaicSize : 0;
bg2.r.mosaic = data.bit(1) ? mosaicSize : 0;
bg3.r.mosaic = data.bit(2) ? mosaicSize : 0;
bg4.r.mosaic = data.bit(3) ? mosaicSize : 0;
return;
}
//BG1SC
case 0x2107: {
bg1.r.screenSize = data.bits(0,1);
bg1.r.screenAddress = data.bits(2,6) << 11;
return;
}
//BG2SC
case 0x2108: {
bg2.r.screenSize = data.bits(0,1);
bg2.r.screenAddress = data.bits(2,6) << 11;
return;
}
//BG3SC
case 0x2109: {
bg3.r.screenSize = data.bits(0,1);
bg3.r.screenAddress = data.bits(2,6) << 11;
return;
}
//BG4SC
case 0x210a: {
bg4.r.screenSize = data.bits(0,1);
bg4.r.screenAddress = data.bits(2,6) << 11;
return;
}
//BG12NBA
case 0x210b: {
bg1.r.tiledataAddress = data.bits(0,2) << 13;
bg2.r.tiledataAddress = data.bits(4,6) << 13;
return;
}
//BG34NBA
case 0x210c: {
bg3.r.tiledataAddress = data.bits(0,2) << 13;
bg4.r.tiledataAddress = data.bits(4,6) << 13;
return;
}
//BG1HOFS
case 0x210d: {
r.hoffsetMode7 = (data << 8) | latch.mode7;
latch.mode7 = data;
bg1.r.hoffset = (data << 8) | (latch.bgofs & ~7) | ((bg1.r.hoffset >> 8) & 7);
latch.bgofs = data;
return;
}
//BG1VOFS
case 0x210e: {
r.voffsetMode7 = (data << 8) | latch.mode7;
latch.mode7 = data;
bg1.r.voffset = (data << 8) | latch.bgofs;
latch.bgofs = data;
return;
}
//BG2HOFS
case 0x210f: {
bg2.r.hoffset = (data << 8) | (latch.bgofs & ~7) | ((bg2.r.hoffset >> 8) & 7);
latch.bgofs = data;
return;
}
//BG2VOFS
case 0x2110: {
bg2.r.voffset = (data << 8) | latch.bgofs;
latch.bgofs = data;
return;
}
//BG3HOFS
case 0x2111: {
bg3.r.hoffset = (data << 8) | (latch.bgofs & ~7) | ((bg3.r.hoffset >> 8) & 7);
latch.bgofs = data;
return;
}
//BG3VOFS
case 0x2112: {
bg3.r.voffset = (data << 8) | latch.bgofs;
latch.bgofs = data;
return;
}
//BG4HOFS
case 0x2113: {
bg4.r.hoffset = (data << 8) | (latch.bgofs & ~7) | ((bg4.r.hoffset >> 8) & 7);
latch.bgofs = data;
return;
}
//BG4VOFS
case 0x2114: {
bg4.r.voffset = (data << 8) | latch.bgofs;
latch.bgofs = data;
return;
}
//VMAIN
case 0x2115: {
r.vramIncrementMode = data & 0x80;
r.vramMapping = (data >> 2) & 3;
switch(data & 3) {
case 0: r.vramIncrementSize = 1; break;
case 1: r.vramIncrementSize = 32; break;
case 2: r.vramIncrementSize = 128; break;
case 3: r.vramIncrementSize = 128; break;
}
return;
}
//VMADDL
case 0x2116: {
r.vramAddress &= 0xff00;
r.vramAddress |= (data << 0);
uint16 address = getVramAddress();
latch.vram.byte(0) = vramRead(address + 0);
latch.vram.byte(1) = vramRead(address + 1);
return;
}
//VMADDH
case 0x2117: {
r.vramAddress &= 0x00ff;
r.vramAddress |= (data << 8);
uint16 address = getVramAddress();
latch.vram.byte(0) = vramRead(address + 0);
latch.vram.byte(1) = vramRead(address + 1);
return;
}
//VMDATAL
case 0x2118: {
uint16 address = getVramAddress() + 0;
vramWrite(address, data);
if(r.vramIncrementMode == 0) r.vramAddress += r.vramIncrementSize;
return;
}
//VMDATAH
case 0x2119: {
uint16 address = getVramAddress() + 1;
vramWrite(address, data);
if(r.vramIncrementMode == 1) r.vramAddress += r.vramIncrementSize;
return;
}
//M7SEL
case 0x211a: {
r.hflipMode7 = data.bit (0);
r.vflipMode7 = data.bit (1);
r.repeatMode7 = data.bits(6,7);
return;
}
//M7A
case 0x211b: {
r.m7a = data << 8 | latch.mode7;
latch.mode7 = data;
return;
}
//M7B
case 0x211c: {
r.m7b = data << 8 | latch.mode7;
latch.mode7 = data;
return;
}
//M7C
case 0x211d: {
r.m7c = data << 8 | latch.mode7;
latch.mode7 = data;
return;
}
//M7D
case 0x211e: {
r.m7d = data << 8 | latch.mode7;
latch.mode7 = data;
return;
}
//M7X
case 0x211f: {
r.m7x = data << 8 | latch.mode7;
latch.mode7 = data;
return;
}
//M7Y
case 0x2120: {
r.m7y = data << 8 | latch.mode7;
latch.mode7 = data;
return;
}
//CGADD
case 0x2121: {
r.cgramAddress = data << 1;
return;
}
//CGDATA
case 0x2122: {
bool l = r.cgramAddress & 1;
uint9 address = r.cgramAddress++;
if(!r.displayDisable
&& vcounter() > 0 && vcounter() < vdisp()
&& hcounter() >= 88 && hcounter() < 1096
) address = latch.cgramAddress;
if(l == 0) {
latch.cgram = data;
} else {
cgramWrite((address & ~1) + 0, latch.cgram);
cgramWrite((address & ~1) + 1, data & 0x7f);
}
return;
}
//W12SEL
case 0x2123: {
window.r.bg1.oneInvert = data.bit(0);
window.r.bg1.oneEnable = data.bit(1);
window.r.bg1.twoInvert = data.bit(2);
window.r.bg1.twoEnable = data.bit(3);
window.r.bg2.oneInvert = data.bit(4);
window.r.bg2.oneEnable = data.bit(5);
window.r.bg2.twoInvert = data.bit(6);
window.r.bg2.twoEnable = data.bit(7);
return;
}
//W34SEL
case 0x2124: {
window.r.bg3.oneInvert = data.bit(0);
window.r.bg3.oneEnable = data.bit(1);
window.r.bg3.twoInvert = data.bit(2);
window.r.bg3.twoEnable = data.bit(3);
window.r.bg4.oneInvert = data.bit(4);
window.r.bg4.oneEnable = data.bit(5);
window.r.bg4.twoInvert = data.bit(6);
window.r.bg4.twoEnable = data.bit(7);
return;
}
//WOBJSEL
case 0x2125: {
window.r.obj.oneInvert = data.bit(0);
window.r.obj.oneEnable = data.bit(1);
window.r.obj.twoInvert = data.bit(2);
window.r.obj.twoEnable = data.bit(3);
window.r.col.oneInvert = data.bit(4);
window.r.col.oneEnable = data.bit(5);
window.r.col.twoInvert = data.bit(6);
window.r.col.twoEnable = data.bit(7);
return;
}
//WH0
case 0x2126: {
window.r.oneLeft = data;
return;
}
//WH1
case 0x2127: {
window.r.oneRight = data;
return;
}
//WH2
case 0x2128: {
window.r.twoLeft = data;
return;
}
//WH3
case 0x2129: {
window.r.twoRight = data;
return;
}
//WBGLOG
case 0x212a: {
window.r.bg1.mask = data.bits(0,1);
window.r.bg2.mask = data.bits(2,3);
window.r.bg3.mask = data.bits(4,5);
window.r.bg4.mask = data.bits(6,7);
return;
}
//WOBJLOG
case 0x212b: {
window.r.obj.mask = data.bits(0,1);
window.r.col.mask = data.bits(2,3);
return;
}
//TM
case 0x212c: {
bg1.r.aboveEnable = data.bit(0);
bg2.r.aboveEnable = data.bit(1);
bg3.r.aboveEnable = data.bit(2);
bg4.r.aboveEnable = data.bit(3);
obj.r.aboveEnable = data.bit(4);
return;
}
//TS
case 0x212d: {
bg1.r.belowEnable = data.bit(0);
bg2.r.belowEnable = data.bit(1);
bg3.r.belowEnable = data.bit(2);
bg4.r.belowEnable = data.bit(3);
obj.r.belowEnable = data.bit(4);
return;
}
//TMW
case 0x212e: {
window.r.bg1.aboveEnable = data.bit(0);
window.r.bg2.aboveEnable = data.bit(1);
window.r.bg3.aboveEnable = data.bit(2);
window.r.bg4.aboveEnable = data.bit(3);
window.r.obj.aboveEnable = data.bit(4);
return;
}
//TSW
case 0x212f: {
window.r.bg1.belowEnable = data.bit(0);
window.r.bg2.belowEnable = data.bit(1);
window.r.bg3.belowEnable = data.bit(2);
window.r.bg4.belowEnable = data.bit(3);
window.r.obj.belowEnable = data.bit(4);
return;
}
//CGWSEL
case 0x2130: {
screen.r.directColor = data.bit (0);
screen.r.blendMode = data.bit (1);
window.r.col.belowMask = data.bits(4,5);
window.r.col.aboveMask = data.bits(6,7);
return;
}
//CGADDSUB
case 0x2131: {
screen.r.bg1.colorEnable = data.bit(0);
screen.r.bg2.colorEnable = data.bit(1);
screen.r.bg3.colorEnable = data.bit(2);
screen.r.bg4.colorEnable = data.bit(3);
screen.r.obj.colorEnable = data.bit(4);
screen.r.back.colorEnable = data.bit(5);
screen.r.colorHalve = data.bit(6);
screen.r.colorMode = data.bit(7);
return;
}
//COLDATA
case 0x2132: {
if(data.bit(5)) screen.r.colorRed = data.bits(0,4);
if(data.bit(6)) screen.r.colorGreen = data.bits(0,4);
if(data.bit(7)) screen.r.colorBlue = data.bits(0,4);
return;
}
//SETINI
case 0x2133: {
r.interlace = data.bit(0);
obj.r.interlace = data.bit(1);
r.overscan = data.bit(2);
r.pseudoHires = data.bit(3);
r.extbg = data.bit(6);
updateVideoMode();
return;
}
}
}
auto PPU::latchCounters() -> void {
cpu.synchronizePPU();
r.hcounter = hdot();
r.vcounter = vcounter();
latch.counters = true;
}
auto PPU::updateVideoMode() -> void {
switch(r.bgMode) {
case 0:
bg1.r.mode = Background::Mode::BPP2;
bg2.r.mode = Background::Mode::BPP2;
bg3.r.mode = Background::Mode::BPP2;
bg4.r.mode = Background::Mode::BPP2;
memory::assign(bg1.r.priority, 8, 11);
memory::assign(bg2.r.priority, 7, 10);
memory::assign(bg3.r.priority, 2, 5);
memory::assign(bg4.r.priority, 1, 4);
memory::assign(obj.r.priority, 3, 6, 9, 12);
break;
case 1:
bg1.r.mode = Background::Mode::BPP4;
bg2.r.mode = Background::Mode::BPP4;
bg3.r.mode = Background::Mode::BPP2;
bg4.r.mode = Background::Mode::Inactive;
if(r.bgPriority) {
memory::assign(bg1.r.priority, 5, 8);
memory::assign(bg2.r.priority, 4, 7);
memory::assign(bg3.r.priority, 1, 10);
memory::assign(obj.r.priority, 2, 3, 6, 9);
} else {
memory::assign(bg1.r.priority, 6, 9);
memory::assign(bg2.r.priority, 5, 8);
memory::assign(bg3.r.priority, 1, 3);
memory::assign(obj.r.priority, 2, 4, 7, 10);
}
break;
case 2:
bg1.r.mode = Background::Mode::BPP4;
bg2.r.mode = Background::Mode::BPP4;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 3, 7);
memory::assign(bg2.r.priority, 1, 5);
memory::assign(obj.r.priority, 2, 4, 6, 8);
break;
case 3:
bg1.r.mode = Background::Mode::BPP8;
bg2.r.mode = Background::Mode::BPP4;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 3, 7);
memory::assign(bg2.r.priority, 1, 5);
memory::assign(obj.r.priority, 2, 4, 6, 8);
break;
case 4:
bg1.r.mode = Background::Mode::BPP8;
bg2.r.mode = Background::Mode::BPP2;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 3, 7);
memory::assign(bg2.r.priority, 1, 5);
memory::assign(obj.r.priority, 2, 4, 6, 8);
break;
case 5:
bg1.r.mode = Background::Mode::BPP4;
bg2.r.mode = Background::Mode::BPP2;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 3, 7);
memory::assign(bg2.r.priority, 1, 5);
memory::assign(obj.r.priority, 2, 4, 6, 8);
break;
case 6:
bg1.r.mode = Background::Mode::BPP4;
bg2.r.mode = Background::Mode::Inactive;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 2, 5);
memory::assign(obj.r.priority, 1, 3, 4, 6);
break;
case 7:
if(!r.extbg) {
bg1.r.mode = Background::Mode::Mode7;
bg2.r.mode = Background::Mode::Inactive;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 2);
memory::assign(obj.r.priority, 1, 3, 4, 5);
} else {
bg1.r.mode = Background::Mode::Mode7;
bg2.r.mode = Background::Mode::Mode7;
bg3.r.mode = Background::Mode::Inactive;
bg4.r.mode = Background::Mode::Inactive;
memory::assign(bg1.r.priority, 3);
memory::assign(bg2.r.priority, 1, 5);
memory::assign(obj.r.priority, 2, 4, 6, 7);
}
break;
}
}