mirror of https://github.com/bsnes-emu/bsnes.git
Update to v106r29 release.
byuu says: Changelog: - sfc/ppu: collapsed folders to a single directory to match all other emulated processors - sfc/ppu-fast: implemented I/O registers
This commit is contained in:
parent
6c8e3c885d
commit
6882bd98cf
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "106.28";
|
||||
static const string Version = "106.29";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
auto PPU::Line::renderBackground(PPU::IO::Background&) -> void {
|
||||
}
|
|
@ -1,3 +1,59 @@
|
|||
auto PPU::latchCounters() -> void {
|
||||
cpu.synchronize(ppu);
|
||||
io.hcounter = hdot();
|
||||
io.vcounter = vcounter();
|
||||
latch.counters = 1;
|
||||
}
|
||||
|
||||
auto PPU::vramAddress() const -> uint15 { //uint15 for 64K VRAM; uint16 for 128K VRAM
|
||||
uint16 address = io.vramAddress;
|
||||
switch(io.vramMapping) {
|
||||
case 0: return address;
|
||||
case 1: return address.bits( 8,15) << 8 | address.bits(0,4) << 3 | address.bits(5,7);
|
||||
case 2: return address.bits( 9,15) << 9 | address.bits(0,5) << 3 | address.bits(6,8);
|
||||
case 3: return address.bits(10,15) << 10 | address.bits(0,6) << 3 | address.bits(7,9);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto PPU::readVRAM() -> uint16 {
|
||||
if(!io.displayDisable && vcounter() < vdisp()) return 0x0000;
|
||||
auto address = vramAddress();
|
||||
return vram[address];
|
||||
}
|
||||
|
||||
auto PPU::writeVRAM(uint1 byte, uint8 data) -> void {
|
||||
if(!io.displayDisable && vcounter() < vdisp()) return;
|
||||
auto address = vramAddress();
|
||||
vram[address].byte(byte) = data;
|
||||
}
|
||||
|
||||
auto PPU::readOAM(uint10 address) -> uint8 {
|
||||
if(!io.displayDisable && vcounter() < vdisp()) address = latch.oamAddress;
|
||||
return readObject(address);
|
||||
}
|
||||
|
||||
auto PPU::writeOAM(uint10 address, uint8 data) -> void {
|
||||
if(!io.displayDisable && vcounter() < vdisp()) address = latch.oamAddress;
|
||||
return writeObject(address, data);
|
||||
}
|
||||
|
||||
auto PPU::readCGRAM(uint1 byte, uint8 address) -> uint8 {
|
||||
if(!io.displayDisable
|
||||
&& vcounter() > 0 && vcounter() < vdisp()
|
||||
&& hcounter() >= 88 && hcounter() < 1096
|
||||
) address = latch.cgramAddress;
|
||||
return cgram[address].byte(byte);
|
||||
}
|
||||
|
||||
auto PPU::writeCGRAM(uint8 address, uint15 data) -> void {
|
||||
if(!io.displayDisable
|
||||
&& vcounter() > 0 && vcounter() < vdisp()
|
||||
&& hcounter() >= 88 && hcounter() < 1096
|
||||
) address = latch.cgramAddress;
|
||||
cgram[address] = data;
|
||||
}
|
||||
|
||||
auto PPU::readIO(uint24 address, uint8 data) -> uint8 {
|
||||
cpu.synchronize(ppu);
|
||||
|
||||
|
@ -12,20 +68,99 @@ auto PPU::readIO(uint24 address, uint8 data) -> uint8 {
|
|||
}
|
||||
|
||||
case 0x2134: { //MPYL
|
||||
uint24 result = (int16)io.m7a * (int8)(io.m7b >> 8);
|
||||
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
||||
return ppu1.mdr = result.byte(0);
|
||||
}
|
||||
|
||||
case 0x2135: { //MPYM
|
||||
uint24 result = (int16)io.m7a * (int8)(io.m7b >> 8);
|
||||
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
||||
return ppu1.mdr = result.byte(1);
|
||||
}
|
||||
|
||||
case 0x2136: { //MPYH
|
||||
uint24 result = (int16)io.m7a * (int8)(io.m7b >> 8);
|
||||
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
||||
return ppu1.mdr = result.byte(2);
|
||||
}
|
||||
|
||||
case 0x2137: { //SLHV
|
||||
if(cpu.pio().bit(7)) latchCounters();
|
||||
return data; //CPU MDR
|
||||
}
|
||||
|
||||
case 0x2138: { //OAMDATAREAD
|
||||
ppu1.mdr = readOAM(io.oamAddress++);
|
||||
oamSetFirstObject();
|
||||
return ppu1.mdr;
|
||||
}
|
||||
|
||||
case 0x2139: { //VMDATALREAD
|
||||
ppu1.mdr = latch.vram.byte(0);
|
||||
if(io.vramIncrementMode == 0) {
|
||||
latch.vram = readVRAM();
|
||||
io.vramAddress += io.vramIncrementSize;
|
||||
}
|
||||
return ppu1.mdr;
|
||||
}
|
||||
|
||||
case 0x213a: { //VMDATAHREAD
|
||||
ppu1.mdr = latch.vram.byte(1);
|
||||
if(io.vramIncrementMode == 1) {
|
||||
latch.vram = readVRAM();
|
||||
io.vramAddress += io.vramIncrementSize;
|
||||
}
|
||||
return ppu1.mdr;
|
||||
}
|
||||
|
||||
case 0x213b: { //CGDATAREAD
|
||||
if(io.cgramAddressLatch++ == 0) {
|
||||
ppu2.mdr.bits(0,7) = readCGRAM(0, io.cgramAddress);
|
||||
} else {
|
||||
ppu2.mdr.bits(0,6) = readCGRAM(1, io.cgramAddress++);
|
||||
}
|
||||
return ppu2.mdr;
|
||||
}
|
||||
|
||||
case 0x213c: { //OPHCT
|
||||
if(latch.hcounter++ == 0) {
|
||||
ppu2.mdr.bits(0,7) = io.hcounter.bits(0,7);
|
||||
} else {
|
||||
ppu2.mdr.bit(0) = io.hcounter.bit(8);
|
||||
}
|
||||
return ppu2.mdr;
|
||||
}
|
||||
|
||||
case 0x213d: { //OPVCT
|
||||
if(latch.vcounter++ == 0) {
|
||||
ppu2.mdr.bits(0,7) = io.vcounter.bits(0,7);
|
||||
} else {
|
||||
ppu2.mdr.bit(0) = io.vcounter.bit(8);
|
||||
}
|
||||
return ppu2.mdr;
|
||||
}
|
||||
|
||||
case 0x213e: { //STAT77
|
||||
ppu1.mdr.bits(0,3) = ppu1.version;
|
||||
ppu1.mdr.bit(5) = 0;
|
||||
ppu1.mdr.bit(6) = io.obj.rangeOver;
|
||||
ppu1.mdr.bit(7) = io.obj.timeOver;
|
||||
return ppu1.mdr;
|
||||
}
|
||||
|
||||
case 0x213f: { //STAT78
|
||||
latch.hcounter = 0;
|
||||
latch.vcounter = 0;
|
||||
ppu2.mdr.bits(0,3) = ppu2.version;
|
||||
ppu2.mdr.bit(4) = Region::PAL(); //0 = NTSC, 1 = PAL
|
||||
if(!cpu.pio().bit(7)) {
|
||||
ppu2.mdr.bit(6) = 1;
|
||||
} else {
|
||||
ppu2.mdr.bit(6) = latch.counters;
|
||||
latch.counters = 0;
|
||||
}
|
||||
ppu2.mdr.bit(7) = field();
|
||||
return ppu2.mdr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -36,41 +171,488 @@ auto PPU::writeIO(uint24 address, uint8 data) -> void {
|
|||
|
||||
switch((uint16)address) {
|
||||
|
||||
case 0x2100: { //INIDISP
|
||||
if(io.displayDisable && vcounter() == vdisp()) oamAddressReset();
|
||||
io.displayBrightness = data.bits(0,3);
|
||||
io.displayDisable = data.bit (7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2101: { //OBSEL
|
||||
io.obj.tiledataAddress = data.bits(0,2) << 13;
|
||||
io.obj.nameselect = data.bits(3,4);
|
||||
io.obj.baseSize = data.bits(5,7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2102: { //OAMADDL
|
||||
io.oamBaseAddress = (io.oamBaseAddress & 0x0200) | data << 1;
|
||||
oamAddressReset();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2103: { //OAMADDH
|
||||
io.oamBaseAddress = data.bit(0) << 9 | (io.oamBaseAddress & 0x01fe);
|
||||
io.oamPriority = data.bit(7);
|
||||
oamAddressReset();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2104: { //OAMDATA
|
||||
uint1 latchBit = io.oamAddress.bit(0);
|
||||
uint10 address = io.oamAddress++;
|
||||
if(latchBit == 0) latch.oam = data;
|
||||
if(address.bit(9)) {
|
||||
writeOAM(address, data);
|
||||
} else if(latchBit == 1) {
|
||||
writeOAM((address & ~1) + 0, latch.oam);
|
||||
writeOAM((address & ~1) + 1, data);
|
||||
}
|
||||
oamSetFirstObject();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2105: { //BGMODE
|
||||
io.bgMode = data.bits(0,2);
|
||||
io.bgPriority = data.bit (3);
|
||||
io.bg1.tileSize = data.bit (4);
|
||||
io.bg2.tileSize = data.bit (5);
|
||||
io.bg3.tileSize = data.bit (6);
|
||||
io.bg4.tileSize = data.bit (7);
|
||||
updateVideoMode();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2106: { //MOSAIC
|
||||
io.bg1.mosaicEnable = data.bit (0);
|
||||
io.bg2.mosaicEnable = data.bit (1);
|
||||
io.bg3.mosaicEnable = data.bit (2);
|
||||
io.bg4.mosaicEnable = data.bit (3);
|
||||
io.mosaicSize = data.bits(4,7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2107: { //BG1SC
|
||||
io.bg1.screenSize = data.bits(0,1);
|
||||
io.bg1.screenAddress = data.bits(2,7) << 10;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2108: { //BG2SC
|
||||
io.bg2.screenSize = data.bits(0,1);
|
||||
io.bg2.screenAddress = data.bits(2,7) << 10;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2109: { //BG3SC
|
||||
io.bg3.screenSize = data.bits(0,1);
|
||||
io.bg3.screenAddress = data.bits(2,7) << 10;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210a: { //BG4SC
|
||||
io.bg4.screenSize = data.bits(0,1);
|
||||
io.bg4.screenAddress = data.bits(2,7) << 10;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210b: { //BG12NBA
|
||||
io.bg1.tiledataAddress = data.bits(0,3) << 12;
|
||||
io.bg2.tiledataAddress = data.bits(4,7) << 12;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210c: { //BG34NBA
|
||||
io.bg3.tiledataAddress = data.bits(0,3) << 12;
|
||||
io.bg4.tiledataAddress = data.bits(4,7) << 12;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210d: { //BG1HOFS
|
||||
io.mode7.hoffset = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
|
||||
io.bg1.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
||||
latch.bgofsPPU1 = data;
|
||||
latch.bgofsPPU2 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210e: { //BG1VOFS
|
||||
io.mode7.voffset = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
|
||||
io.bg1.voffset = data << 8 | latch.bgofsPPU1;
|
||||
latch.bgofsPPU1 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210f: { //BG2HOFS
|
||||
io.bg2.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
||||
latch.bgofsPPU1 = data;
|
||||
latch.bgofsPPU2 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2110: { //BG2VOFS
|
||||
io.bg2.voffset = data << 8 | latch.bgofsPPU1;
|
||||
latch.bgofsPPU1 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2111: { //BG3HOFS
|
||||
io.bg3.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
||||
latch.bgofsPPU1 = data;
|
||||
latch.bgofsPPU2 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2112: { //BG3VOFS
|
||||
io.bg3.voffset = data << 8 | latch.bgofsPPU1;
|
||||
latch.bgofsPPU1 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2113: { //BG4HOFS
|
||||
io.bg4.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
||||
latch.bgofsPPU1 = data;
|
||||
latch.bgofsPPU2 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2114: { //BG4VOFS
|
||||
io.bg4.voffset = data << 8 | latch.bgofsPPU1;
|
||||
latch.bgofsPPU1 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2115: { //VMAIN
|
||||
static const uint size[4] = {1, 32, 128, 128};
|
||||
io.vramIncrementSize = size[data.bits(0,1)];
|
||||
io.vramMapping = data.bits(2,3);
|
||||
io.vramIncrementMode = data.bit (7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2116: { //VMADDL
|
||||
io.vramAddress.byte(0) = data;
|
||||
latch.vram = readVRAM();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2117: { //VMADDH
|
||||
io.vramAddress.byte(1) = data;
|
||||
latch.vram = readVRAM();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2118: { //VMDATAL
|
||||
writeVRAM(0, data);
|
||||
if(io.vramIncrementMode == 0) io.vramAddress += io.vramIncrementSize;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2119: { //VMDATAH
|
||||
writeVRAM(1, data);
|
||||
if(io.vramIncrementMode == 1) io.vramAddress += io.vramIncrementSize;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x211b: { //M7A
|
||||
io.m7a = data << 8 | latch.mode7;
|
||||
io.mode7.a = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x211c: { //M7B
|
||||
io.m7b = data << 8 | latch.mode7;
|
||||
io.mode7.b = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x211d: { //M7C
|
||||
io.m7c = data << 8 | latch.mode7;
|
||||
io.mode7.c = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x211e: { //M7D
|
||||
io.m7d = data << 8 | latch.mode7;
|
||||
io.mode7.d = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x211f: { //M7X
|
||||
io.m7x = data << 8 | latch.mode7;
|
||||
io.mode7.x = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2120: { //M7Y
|
||||
io.m7y = data << 8 | latch.mode7;
|
||||
io.mode7.y = data << 8 | latch.mode7;
|
||||
latch.mode7 = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2121: { //CGADD
|
||||
io.cgramAddress = data;
|
||||
io.cgramAddressLatch = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2122: { //CGDATA
|
||||
if(io.cgramAddressLatch++ == 0) {
|
||||
latch.cgram = data;
|
||||
} else {
|
||||
writeCGRAM(io.cgramAddress++, data.bits(0,6) << 8 | latch.cgram);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2123: { //W12SEL
|
||||
io.bg1.window.oneInvert = data.bit(0);
|
||||
io.bg1.window.oneEnable = data.bit(1);
|
||||
io.bg1.window.twoInvert = data.bit(2);
|
||||
io.bg1.window.twoEnable = data.bit(3);
|
||||
io.bg2.window.oneInvert = data.bit(4);
|
||||
io.bg2.window.oneEnable = data.bit(5);
|
||||
io.bg2.window.twoInvert = data.bit(6);
|
||||
io.bg2.window.twoEnable = data.bit(7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2124: { //W34SEL
|
||||
io.bg3.window.oneInvert = data.bit(0);
|
||||
io.bg3.window.oneEnable = data.bit(1);
|
||||
io.bg3.window.twoInvert = data.bit(2);
|
||||
io.bg3.window.twoEnable = data.bit(3);
|
||||
io.bg4.window.oneInvert = data.bit(4);
|
||||
io.bg4.window.oneEnable = data.bit(5);
|
||||
io.bg4.window.twoInvert = data.bit(6);
|
||||
io.bg4.window.twoEnable = data.bit(7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2125: { //WOBJSEL
|
||||
io.obj.window.oneInvert = data.bit(0);
|
||||
io.obj.window.oneEnable = data.bit(1);
|
||||
io.obj.window.twoInvert = data.bit(2);
|
||||
io.obj.window.twoEnable = data.bit(3);
|
||||
io.col.window.oneInvert = data.bit(4);
|
||||
io.col.window.oneEnable = data.bit(5);
|
||||
io.col.window.twoInvert = data.bit(6);
|
||||
io.col.window.twoEnable = data.bit(7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2126: { //WH0
|
||||
io.window.oneLeft = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2127: { //WH1
|
||||
io.window.oneRight = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2128: { //WH2
|
||||
io.window.twoLeft = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2129: { //WH3
|
||||
io.window.twoRight = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212a: { //WBGLOG
|
||||
io.bg1.window.mask = data.bits(0,1);
|
||||
io.bg2.window.mask = data.bits(2,3);
|
||||
io.bg3.window.mask = data.bits(4,5);
|
||||
io.bg4.window.mask = data.bits(6,7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212b: { //WOBJLOG
|
||||
io.obj.window.mask = data.bits(0,1);
|
||||
io.col.window.mask = data.bits(2,3);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212c: { //TM
|
||||
io.bg1.aboveEnable = data.bit(0);
|
||||
io.bg2.aboveEnable = data.bit(1);
|
||||
io.bg3.aboveEnable = data.bit(2);
|
||||
io.bg4.aboveEnable = data.bit(3);
|
||||
io.obj.aboveEnable = data.bit(4);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212d: { //TS
|
||||
io.bg1.belowEnable = data.bit(0);
|
||||
io.bg2.belowEnable = data.bit(1);
|
||||
io.bg3.belowEnable = data.bit(2);
|
||||
io.bg4.belowEnable = data.bit(3);
|
||||
io.obj.belowEnable = data.bit(4);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212e: { //TMW
|
||||
io.bg1.window.aboveEnable = data.bit(0);
|
||||
io.bg2.window.aboveEnable = data.bit(1);
|
||||
io.bg3.window.aboveEnable = data.bit(2);
|
||||
io.bg4.window.aboveEnable = data.bit(3);
|
||||
io.obj.window.aboveEnable = data.bit(4);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212f: { //TSW
|
||||
io.bg1.window.belowEnable = data.bit(0);
|
||||
io.bg2.window.belowEnable = data.bit(1);
|
||||
io.bg3.window.belowEnable = data.bit(2);
|
||||
io.bg4.window.belowEnable = data.bit(3);
|
||||
io.obj.window.belowEnable = data.bit(4);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2130: { //CGWSEL
|
||||
io.col.directColor = data.bit (0);
|
||||
io.col.blendMode = data.bit (1);
|
||||
io.col.window.belowMask = data.bits(4,5);
|
||||
io.col.window.aboveMask = data.bits(6,7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2131: { //CGADDSUB
|
||||
io.bg1.colorEnable = data.bit(0);
|
||||
io.bg2.colorEnable = data.bit(1);
|
||||
io.bg3.colorEnable = data.bit(2);
|
||||
io.bg4.colorEnable = data.bit(3);
|
||||
io.obj.colorEnable = data.bit(4);
|
||||
io.col.colorEnable = data.bit(5);
|
||||
io.col.colorHalve = data.bit(6);
|
||||
io.col.colorMode = data.bit(7);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2132: { //COLDATA
|
||||
if(data.bit(5)) io.col.colorRed = data.bits(0,4);
|
||||
if(data.bit(6)) io.col.colorGreen = data.bits(0,4);
|
||||
if(data.bit(7)) io.col.colorBlue = data.bits(0,4);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2133: { //SETINI
|
||||
io.interlace = data.bit(0);
|
||||
io.obj.interlace = data.bit(1);
|
||||
io.overscan = data.bit(2);
|
||||
io.pseudoHires = data.bit(3);
|
||||
io.extbg = data.bit(6);
|
||||
updateVideoMode();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::updateVideoMode() -> void {
|
||||
switch(io.bgMode) {
|
||||
case 0:
|
||||
io.bg1.tileMode = TileMode::BPP2;
|
||||
io.bg2.tileMode = TileMode::BPP2;
|
||||
io.bg3.tileMode = TileMode::BPP2;
|
||||
io.bg4.tileMode = TileMode::BPP2;
|
||||
memory::assign(io.bg1.priority, 8, 11);
|
||||
memory::assign(io.bg2.priority, 7, 10);
|
||||
memory::assign(io.bg3.priority, 2, 5);
|
||||
memory::assign(io.bg4.priority, 1, 4);
|
||||
memory::assign(io.obj.priority, 3, 6, 9, 12);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
io.bg1.tileMode = TileMode::BPP4;
|
||||
io.bg2.tileMode = TileMode::BPP4;
|
||||
io.bg3.tileMode = TileMode::BPP2;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
if(io.bgPriority) {
|
||||
memory::assign(io.bg1.priority, 5, 8);
|
||||
memory::assign(io.bg2.priority, 4, 7);
|
||||
memory::assign(io.bg3.priority, 1, 10);
|
||||
memory::assign(io.obj.priority, 2, 3, 6, 9);
|
||||
} else {
|
||||
memory::assign(io.bg1.priority, 6, 9);
|
||||
memory::assign(io.bg2.priority, 5, 8);
|
||||
memory::assign(io.bg3.priority, 1, 3);
|
||||
memory::assign(io.obj.priority, 2, 4, 7, 10);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
io.bg1.tileMode = TileMode::BPP4;
|
||||
io.bg2.tileMode = TileMode::BPP4;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
io.bg1.tileMode = TileMode::BPP8;
|
||||
io.bg2.tileMode = TileMode::BPP4;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
io.bg1.tileMode = TileMode::BPP8;
|
||||
io.bg2.tileMode = TileMode::BPP2;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
io.bg1.tileMode = TileMode::BPP4;
|
||||
io.bg2.tileMode = TileMode::BPP2;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3, 7);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 8);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
io.bg1.tileMode = TileMode::BPP4;
|
||||
io.bg2.tileMode = TileMode::Inactive;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 2, 5);
|
||||
memory::assign(io.obj.priority, 1, 3, 4, 6);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
if(!io.extbg) {
|
||||
io.bg1.tileMode = TileMode::Mode7;
|
||||
io.bg2.tileMode = TileMode::Inactive;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 2);
|
||||
memory::assign(io.obj.priority, 1, 3, 4, 5);
|
||||
} else {
|
||||
io.bg1.tileMode = TileMode::Mode7;
|
||||
io.bg2.tileMode = TileMode::Mode7;
|
||||
io.bg3.tileMode = TileMode::Inactive;
|
||||
io.bg4.tileMode = TileMode::Inactive;
|
||||
memory::assign(io.bg1.priority, 3);
|
||||
memory::assign(io.bg2.priority, 1, 5);
|
||||
memory::assign(io.obj.priority, 2, 4, 6, 7);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
auto PPU::Line::render() -> void {
|
||||
renderWindow(io.bg1.window);
|
||||
renderWindow(io.bg2.window);
|
||||
renderWindow(io.bg3.window);
|
||||
renderWindow(io.bg4.window);
|
||||
renderWindow(io.obj.window);
|
||||
renderWindow(io.col.window);
|
||||
renderBackground(io.bg1);
|
||||
renderBackground(io.bg2);
|
||||
renderBackground(io.bg3);
|
||||
renderBackground(io.bg4);
|
||||
renderObject(io.obj);
|
||||
|
||||
if(io.displayDisable) {
|
||||
for(uint x : range(512)) {
|
||||
outputLo[x] = 0x7ffff;
|
||||
outputHi[x] = 0x7ffff;
|
||||
outputLo[x] = 0;
|
||||
outputHi[x] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
auto PPU::readOAM(uint10 address) -> uint8 {
|
||||
auto PPU::Line::renderObject(PPU::IO::Object&) -> void {
|
||||
}
|
||||
|
||||
auto PPU::oamAddressReset() -> void {
|
||||
}
|
||||
|
||||
auto PPU::oamSetFirstObject() -> void {
|
||||
}
|
||||
|
||||
auto PPU::readObject(uint10 address) -> uint8 {
|
||||
if(!address.bit(9)) {
|
||||
uint n = address >> 2; //object#
|
||||
address &= 3;
|
||||
|
@ -27,7 +36,7 @@ auto PPU::readOAM(uint10 address) -> uint8 {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::writeOAM(uint10 address, uint8 data) -> void {
|
||||
auto PPU::writeObject(uint10 address, uint8 data) -> void {
|
||||
if(!address.bit(9)) {
|
||||
uint n = address >> 2; //object#
|
||||
if(address == 0) { object[n].x.bits(0,7) = data; return; }
|
||||
|
|
|
@ -4,14 +4,20 @@ namespace SuperFamicom {
|
|||
|
||||
PPU ppu;
|
||||
#include "io.cpp"
|
||||
#include "object.cpp"
|
||||
#include "line.cpp"
|
||||
#include "background.cpp"
|
||||
#include "object.cpp"
|
||||
#include "window.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include <sfc/ppu/counter/serialization.cpp>
|
||||
|
||||
PPU::PPU() {
|
||||
ppu1.version = 1;
|
||||
ppu2.version = 3;
|
||||
|
||||
output = new uint32[512 * 512];
|
||||
output += 16 * 512; //overscan offset
|
||||
|
||||
for(uint y : range(240)) {
|
||||
lines[y].y = y;
|
||||
lines[y].outputLo = output + (y * 2 + 0) * 512;
|
||||
|
@ -36,7 +42,7 @@ auto PPU::step(uint clocks) -> void {
|
|||
|
||||
auto PPU::main() -> void {
|
||||
scanline();
|
||||
uint y = vcounter();
|
||||
uint y = PPUcounter::vcounter();
|
||||
|
||||
step(512);
|
||||
if(y >= 1 && y <= vdisp()) {
|
||||
|
@ -45,16 +51,15 @@ auto PPU::main() -> void {
|
|||
}
|
||||
|
||||
step(624);
|
||||
|
||||
step(lineclocks() - 512 - 624);
|
||||
step(PPUcounter::lineclocks() - PPUcounter::hcounter());
|
||||
}
|
||||
|
||||
auto PPU::scanline() -> void {
|
||||
if(vcounter() == 0) {
|
||||
if(PPUcounter::vcounter() == 0) {
|
||||
frame();
|
||||
}
|
||||
|
||||
if(vcounter() == 241) {
|
||||
if(PPUcounter::vcounter() == 241) {
|
||||
#pragma omp parallel for
|
||||
for(uint y = 1; y < vdisp(); y++) {
|
||||
lines[y].render();
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
//performance-focused, scanline-based, parallelized implementation of PPU
|
||||
|
||||
//limitations:
|
||||
//* mid-scanline effects not support
|
||||
//* mid-frame OAM changes not supported
|
||||
|
||||
struct PPU : Thread, PPUcounter {
|
||||
//as a scanline-based renderer, PPU::PPUcounter values are not cycle-accurate
|
||||
alwaysinline auto field() const -> bool { return cpu.field(); }
|
||||
alwaysinline auto vcounter() const -> uint16 { return cpu.vcounter(); }
|
||||
alwaysinline auto hcounter() const -> uint16 { return cpu.hcounter(); }
|
||||
alwaysinline auto hdot() const -> uint16 { return cpu.hdot(); }
|
||||
alwaysinline auto lineclocks() const -> uint16 { return cpu.lineclocks(); }
|
||||
|
||||
alwaysinline auto interlace() const -> bool { return false; }
|
||||
alwaysinline auto overscan() const -> bool { return false; }
|
||||
alwaysinline auto vdisp() const -> uint { return 225; }
|
||||
alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; }
|
||||
|
||||
//ppu.cpp
|
||||
PPU();
|
||||
|
@ -16,8 +29,6 @@ struct PPU : Thread, PPUcounter {
|
|||
auto load(Markup::Node) -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
auto latchCounters() -> void {}
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
|
@ -32,44 +43,147 @@ public:
|
|||
} ppu1, ppu2;
|
||||
|
||||
struct Latch {
|
||||
uint16 vram;
|
||||
uint8 oam;
|
||||
uint8 cgram;
|
||||
uint8 bgofsPPU1;
|
||||
uint8 bgofsPPU2;
|
||||
uint8 mode7;
|
||||
uint1 counters;
|
||||
uint1 hcounter; //hdot
|
||||
uint1 vcounter;
|
||||
|
||||
uint10 oamAddress;
|
||||
uint8 cgramAddress;
|
||||
} latch;
|
||||
|
||||
enum : uint {
|
||||
INIDISP = 0x00, OBSEL = 0x01, OAMADDL = 0x02, OAMADDH = 0x03,
|
||||
OAMDATA = 0x04, BGMODE = 0x05, MOSAIC = 0x06, BG1SC = 0x07,
|
||||
BG2SC = 0x08, BG3SC = 0x09, BG4SC = 0x0a, BG12NBA = 0x0b,
|
||||
BG34NBA = 0x0c, BG1HOFS = 0x0d, BG1VOFS = 0x0e, BG2HOFS = 0x0f,
|
||||
BG2VOFS = 0x10, BG3HOFS = 0x11, BG3VOFS = 0x12, BG4HOFS = 0x13,
|
||||
BG4VOFS = 0x14, VMAIN = 0x15, VMADDL = 0x16, VMADDH = 0x17,
|
||||
VMDATAL = 0x18, VMDATAH = 0x19, M7SEL = 0x1a, M7A = 0x1b,
|
||||
M7B = 0x1c, M7C = 0x1d, M7D = 0x1e, M7X = 0x1f,
|
||||
M7Y = 0x20, CGADD = 0x21, CGDATA = 0x22, W12SEL = 0x23,
|
||||
W34SEL = 0x24, WOBJSEL = 0x25, WH0 = 0x26, WH1 = 0x27,
|
||||
WH2 = 0x28, WH3 = 0x29, WBGLOG = 0x2a, WOBJLOG = 0x2b,
|
||||
TM = 0x2c, TS = 0x2d, TMW = 0x2e, TSW = 0x2f,
|
||||
CGWSEL = 0x30, CGADDSUB = 0x31, COLDATA = 0x32, SETINI = 0x33,
|
||||
MPYL = 0x34, MPYM = 0x35, MPYH = 0x36, SLHV = 0x37,
|
||||
OAMDATAREAD = 0x38, VMDATALREAD = 0x39, VMDATAHREAD = 0x3a, CGDATAREAD = 0x3b,
|
||||
OPHCT = 0x3c, OPVCT = 0x3d, STAT77 = 0x3e, STAT78 = 0x3f,
|
||||
};
|
||||
|
||||
//io.cpp
|
||||
auto latchCounters() -> void;
|
||||
alwaysinline auto vramAddress() const -> uint15;
|
||||
alwaysinline auto readVRAM() -> uint16;
|
||||
alwaysinline auto writeVRAM(uint1 byte, uint8 data) -> void;
|
||||
alwaysinline auto readOAM(uint10 address) -> uint8;
|
||||
alwaysinline auto writeOAM(uint10 address, uint8 data) -> void;
|
||||
alwaysinline auto readCGRAM(uint1 byte, uint8 address) -> uint8;
|
||||
alwaysinline auto writeCGRAM(uint8 address, uint15 data) -> void;
|
||||
auto readIO(uint24 address, uint8 data) -> uint8;
|
||||
auto writeIO(uint24 address, uint8 data) -> void;
|
||||
auto updateVideoMode() -> void;
|
||||
|
||||
struct TileMode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
||||
struct TileSize { enum : uint { Size8x8, Size16x16 }; };
|
||||
struct ScreenMode { enum : uint { Above, Below }; };
|
||||
struct ScreenSize { enum : uint { Size32x32, Size32x64, Size64x32, Size64x64 }; };
|
||||
|
||||
struct IO {
|
||||
uint16 m7a;
|
||||
uint16 m7b;
|
||||
uint16 m7c;
|
||||
uint16 m7d;
|
||||
uint16 m7x;
|
||||
uint16 m7y;
|
||||
uint1 displayDisable;
|
||||
uint4 displayBrightness;
|
||||
uint10 oamBaseAddress;
|
||||
uint10 oamAddress;
|
||||
uint1 oamPriority;
|
||||
uint1 bgPriority;
|
||||
uint3 bgMode;
|
||||
uint4 mosaicSize;
|
||||
uint1 vramIncrementMode;
|
||||
uint2 vramMapping;
|
||||
uint8 vramIncrementSize;
|
||||
uint16 vramAddress;
|
||||
uint8 cgramAddress;
|
||||
uint1 cgramAddressLatch;
|
||||
uint9 hcounter; //hdot
|
||||
uint9 vcounter;
|
||||
uint1 interlace;
|
||||
uint1 overscan;
|
||||
uint1 pseudoHires;
|
||||
uint1 extbg;
|
||||
|
||||
struct WindowLayer {
|
||||
uint1 oneEnable;
|
||||
uint1 oneInvert;
|
||||
uint1 twoEnable;
|
||||
uint1 twoInvert;
|
||||
uint2 mask;
|
||||
uint1 aboveEnable;
|
||||
uint1 belowEnable;
|
||||
};
|
||||
|
||||
struct WindowColor {
|
||||
uint1 oneEnable;
|
||||
uint1 oneInvert;
|
||||
uint1 twoEnable;
|
||||
uint1 twoInvert;
|
||||
uint2 mask;
|
||||
uint2 aboveMask;
|
||||
uint2 belowMask;
|
||||
};
|
||||
|
||||
struct Window {
|
||||
uint8 oneLeft;
|
||||
uint8 oneRight;
|
||||
uint8 twoLeft;
|
||||
uint8 twoRight;
|
||||
} window;
|
||||
|
||||
struct Mode7 {
|
||||
uint16 a;
|
||||
uint16 b;
|
||||
uint16 c;
|
||||
uint16 d;
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint16 hoffset;
|
||||
uint16 voffset;
|
||||
} mode7;
|
||||
|
||||
struct Background {
|
||||
WindowLayer window;
|
||||
uint1 aboveEnable;
|
||||
uint1 belowEnable;
|
||||
uint1 colorEnable;
|
||||
uint1 mosaicEnable;
|
||||
uint15 tiledataAddress;
|
||||
uint15 screenAddress;
|
||||
uint2 screenSize;
|
||||
uint1 tileSize;
|
||||
uint16 hoffset;
|
||||
uint16 voffset;
|
||||
uint3 tileMode;
|
||||
uint4 priority[2];
|
||||
} bg1, bg2, bg3, bg4;
|
||||
|
||||
struct Object {
|
||||
WindowLayer window;
|
||||
uint1 aboveEnable;
|
||||
uint1 belowEnable;
|
||||
uint1 colorEnable;
|
||||
uint1 interlace;
|
||||
uint3 baseSize;
|
||||
uint2 nameselect;
|
||||
uint15 tiledataAddress;
|
||||
uint7 firstObject;
|
||||
uint1 rangeOver;
|
||||
uint1 timeOver;
|
||||
uint4 priority[4];
|
||||
} obj;
|
||||
|
||||
struct Color {
|
||||
WindowColor window;
|
||||
uint1 colorEnable;
|
||||
uint1 directColor;
|
||||
uint1 blendMode;
|
||||
uint1 colorHalve;
|
||||
uint1 colorMode;
|
||||
uint5 colorRed;
|
||||
uint5 colorGreen;
|
||||
uint5 colorBlue;
|
||||
} col;
|
||||
} io;
|
||||
|
||||
//object.cpp
|
||||
auto readOAM(uint10 address) -> uint8;
|
||||
auto writeOAM(uint10 address, uint8 data) -> void;
|
||||
auto oamAddressReset() -> void;
|
||||
auto oamSetFirstObject() -> void;
|
||||
auto readObject(uint10 address) -> uint8;
|
||||
auto writeObject(uint10 address, uint8 data) -> void;
|
||||
|
||||
struct Object {
|
||||
uint9 x;
|
||||
|
@ -84,15 +198,25 @@ public:
|
|||
} object[128];
|
||||
|
||||
//bitplane -> bitmap tile caches
|
||||
uint8 vram2bpp[4096 * 64];
|
||||
uint8 vram4bpp[2048 * 64];
|
||||
uint8 vram8bpp[1024 * 64];
|
||||
uint8 vram2bpp[4096 * 8 * 8];
|
||||
uint8 vram4bpp[2048 * 8 * 8];
|
||||
uint8 vram8bpp[1024 * 8 * 8];
|
||||
|
||||
struct Line {
|
||||
//line.cpp
|
||||
auto render() -> void;
|
||||
|
||||
uint y = 0;
|
||||
//background.cpp
|
||||
auto renderBackground(PPU::IO::Background&) -> void;
|
||||
|
||||
//object.cpp
|
||||
auto renderObject(PPU::IO::Object&) -> void;
|
||||
|
||||
//window.cpp
|
||||
auto renderWindow(PPU::IO::WindowLayer&) -> void;
|
||||
auto renderWindow(PPU::IO::WindowColor&) -> void;
|
||||
|
||||
uint9 y;
|
||||
uint32* outputLo = nullptr;
|
||||
uint32* outputHi = nullptr;
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
auto PPU::Line::renderWindow(PPU::IO::WindowLayer&) -> void {
|
||||
}
|
||||
|
||||
auto PPU::Line::renderWindow(PPU::IO::WindowColor&) -> void {
|
||||
}
|
|
@ -11,14 +11,14 @@ auto PPU::addressVRAM() const -> uint16 {
|
|||
|
||||
auto PPU::readVRAM() -> uint16 {
|
||||
if(!io.displayDisable && vcounter() < vdisp()) return 0x0000;
|
||||
auto addr = addressVRAM();
|
||||
return vram[addr];
|
||||
auto address = addressVRAM();
|
||||
return vram[address];
|
||||
}
|
||||
|
||||
auto PPU::writeVRAM(bool byte, uint8 data) -> void {
|
||||
if(!io.displayDisable && vcounter() < vdisp()) return;
|
||||
auto addr = addressVRAM();
|
||||
vram[addr].byte(byte) = data;
|
||||
auto address = addressVRAM();
|
||||
vram[address].byte(byte) = data;
|
||||
}
|
||||
|
||||
auto PPU::readOAM(uint10 addr) -> uint8 {
|
||||
|
@ -154,7 +154,6 @@ auto PPU::readIO(uint24 addr, uint8 data) -> uint8 {
|
|||
case 0x213f: {
|
||||
latch.hcounter = 0;
|
||||
latch.vcounter = 0;
|
||||
|
||||
ppu2.mdr.bits(0,3) = ppu2.version;
|
||||
ppu2.mdr.bit ( 4) = Region::PAL(); //0 = NTSC, 1 = PAL
|
||||
if(!cpu.pio().bit(7)) {
|
||||
|
@ -195,7 +194,7 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
|||
|
||||
//OAMADDL
|
||||
case 0x2102: {
|
||||
io.oamBaseAddress = (io.oamBaseAddress & 0x0200) | (data << 1);
|
||||
io.oamBaseAddress = (io.oamBaseAddress & 0x0200) | data << 1;
|
||||
obj.addressReset();
|
||||
return;
|
||||
}
|
||||
|
@ -210,9 +209,8 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
|||
|
||||
//OAMDATA
|
||||
case 0x2104: {
|
||||
bool latchBit = io.oamAddress & 1;
|
||||
uint1 latchBit = io.oamAddress.bit(0);
|
||||
uint10 address = io.oamAddress++;
|
||||
|
||||
if(latchBit == 0) latch.oam = data;
|
||||
if(address.bit(9)) {
|
||||
writeOAM(address, data);
|
||||
|
|
|
@ -5,10 +5,10 @@ namespace SuperFamicom {
|
|||
PPU ppu;
|
||||
|
||||
#include "io.cpp"
|
||||
#include "background/background.cpp"
|
||||
#include "object/object.cpp"
|
||||
#include "window/window.cpp"
|
||||
#include "screen/screen.cpp"
|
||||
#include "background.cpp"
|
||||
#include "object.cpp"
|
||||
#include "window.cpp"
|
||||
#include "screen.cpp"
|
||||
#include "serialization.cpp"
|
||||
#include "counter/serialization.cpp"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
struct PPU : Thread, PPUcounter {
|
||||
alwaysinline auto interlace() const -> bool { return display.interlace; }
|
||||
alwaysinline auto overscan() const -> bool { return display.overscan; }
|
||||
alwaysinline auto vdisp() const -> uint { return io.overscan ? 240 : 225; }
|
||||
alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; }
|
||||
|
||||
PPU();
|
||||
~PPU();
|
||||
|
@ -136,10 +136,10 @@ private:
|
|||
uint16 vcounter;
|
||||
} io;
|
||||
|
||||
#include "background/background.hpp"
|
||||
#include "object/object.hpp"
|
||||
#include "window/window.hpp"
|
||||
#include "screen/screen.hpp"
|
||||
#include "background.hpp"
|
||||
#include "object.hpp"
|
||||
#include "window.hpp"
|
||||
#include "screen.hpp"
|
||||
|
||||
Background bg1;
|
||||
Background bg2;
|
||||
|
|
Loading…
Reference in New Issue