From 6882bd98cfa952c407d79b03005cad29f434006d Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 27 May 2018 09:04:43 +1000 Subject: [PATCH] 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 --- higan/emulator/emulator.hpp | 2 +- higan/sfc/ppu-fast/background.cpp | 2 + higan/sfc/ppu-fast/io.cpp | 600 +++++++++++++++++- higan/sfc/ppu-fast/line.cpp | 21 +- higan/sfc/ppu-fast/object.cpp | 13 +- higan/sfc/ppu-fast/ppu.cpp | 17 +- higan/sfc/ppu-fast/ppu.hpp | 194 +++++- higan/sfc/ppu-fast/window.cpp | 5 + higan/sfc/ppu/{background => }/background.cpp | 0 higan/sfc/ppu/{background => }/background.hpp | 0 higan/sfc/ppu/io.cpp | 22 +- higan/sfc/ppu/{background => }/mode7.cpp | 0 higan/sfc/ppu/{object => }/oam.cpp | 0 higan/sfc/ppu/{object => }/object.cpp | 0 higan/sfc/ppu/{object => }/object.hpp | 0 higan/sfc/ppu/ppu.cpp | 8 +- higan/sfc/ppu/ppu.hpp | 10 +- higan/sfc/ppu/{screen => }/screen.cpp | 0 higan/sfc/ppu/{screen => }/screen.hpp | 0 higan/sfc/ppu/{window => }/window.cpp | 0 higan/sfc/ppu/{window => }/window.hpp | 0 21 files changed, 817 insertions(+), 77 deletions(-) create mode 100644 higan/sfc/ppu-fast/background.cpp create mode 100644 higan/sfc/ppu-fast/window.cpp rename higan/sfc/ppu/{background => }/background.cpp (100%) rename higan/sfc/ppu/{background => }/background.hpp (100%) rename higan/sfc/ppu/{background => }/mode7.cpp (100%) rename higan/sfc/ppu/{object => }/oam.cpp (100%) rename higan/sfc/ppu/{object => }/object.cpp (100%) rename higan/sfc/ppu/{object => }/object.hpp (100%) rename higan/sfc/ppu/{screen => }/screen.cpp (100%) rename higan/sfc/ppu/{screen => }/screen.hpp (100%) rename higan/sfc/ppu/{window => }/window.cpp (100%) rename higan/sfc/ppu/{window => }/window.hpp (100%) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 72946255..7ac6d561 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -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/"; diff --git a/higan/sfc/ppu-fast/background.cpp b/higan/sfc/ppu-fast/background.cpp new file mode 100644 index 00000000..630d8a77 --- /dev/null +++ b/higan/sfc/ppu-fast/background.cpp @@ -0,0 +1,2 @@ +auto PPU::Line::renderBackground(PPU::IO::Background&) -> void { +} diff --git a/higan/sfc/ppu-fast/io.cpp b/higan/sfc/ppu-fast/io.cpp index b17ac029..d3981c25 100644 --- a/higan/sfc/ppu-fast/io.cpp +++ b/higan/sfc/ppu-fast/io.cpp @@ -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; } } diff --git a/higan/sfc/ppu-fast/line.cpp b/higan/sfc/ppu-fast/line.cpp index c2ec460d..da3ed91f 100644 --- a/higan/sfc/ppu-fast/line.cpp +++ b/higan/sfc/ppu-fast/line.cpp @@ -1,6 +1,21 @@ auto PPU::Line::render() -> void { - for(uint x : range(512)) { - outputLo[x] = 0x7ffff; - outputHi[x] = 0x7ffff; + 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] = 0; + outputHi[x] = 0; + } + return; } } diff --git a/higan/sfc/ppu-fast/object.cpp b/higan/sfc/ppu-fast/object.cpp index 8f6bf2bd..8ec9b702 100644 --- a/higan/sfc/ppu-fast/object.cpp +++ b/higan/sfc/ppu-fast/object.cpp @@ -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; } diff --git a/higan/sfc/ppu-fast/ppu.cpp b/higan/sfc/ppu-fast/ppu.cpp index 25eabab4..7c2ea997 100644 --- a/higan/sfc/ppu-fast/ppu.cpp +++ b/higan/sfc/ppu-fast/ppu.cpp @@ -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 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(); diff --git a/higan/sfc/ppu-fast/ppu.hpp b/higan/sfc/ppu-fast/ppu.hpp index f616d3f0..27650faf 100644 --- a/higan/sfc/ppu-fast/ppu.hpp +++ b/higan/sfc/ppu-fast/ppu.hpp @@ -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 { - uint8 mode7; + 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; diff --git a/higan/sfc/ppu-fast/window.cpp b/higan/sfc/ppu-fast/window.cpp new file mode 100644 index 00000000..a7cd7227 --- /dev/null +++ b/higan/sfc/ppu-fast/window.cpp @@ -0,0 +1,5 @@ +auto PPU::Line::renderWindow(PPU::IO::WindowLayer&) -> void { +} + +auto PPU::Line::renderWindow(PPU::IO::WindowColor&) -> void { +} diff --git a/higan/sfc/ppu/background/background.cpp b/higan/sfc/ppu/background.cpp similarity index 100% rename from higan/sfc/ppu/background/background.cpp rename to higan/sfc/ppu/background.cpp diff --git a/higan/sfc/ppu/background/background.hpp b/higan/sfc/ppu/background.hpp similarity index 100% rename from higan/sfc/ppu/background/background.hpp rename to higan/sfc/ppu/background.hpp diff --git a/higan/sfc/ppu/io.cpp b/higan/sfc/ppu/io.cpp index 8f855c35..eb1a9649 100644 --- a/higan/sfc/ppu/io.cpp +++ b/higan/sfc/ppu/io.cpp @@ -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 { @@ -126,7 +126,7 @@ auto PPU::readIO(uint24 addr, uint8 data) -> uint8 { if(latch.hcounter++ == 0) { ppu2.mdr.bits(0,7) = io.hcounter.bits(0,7); } else { - ppu2.mdr.bit (0 ) = io.hcounter.bit ( 8); + ppu2.mdr.bit (0 ) = io.hcounter.bit (8 ); } return ppu2.mdr; } @@ -136,7 +136,7 @@ auto PPU::readIO(uint24 addr, uint8 data) -> uint8 { if(latch.vcounter++ == 0) { ppu2.mdr.bits(0,7) = io.vcounter.bits(0,7); } else { - ppu2.mdr.bit (0 ) = io.vcounter.bit ( 8); + ppu2.mdr.bit (0 ) = io.vcounter.bit (8 ); } return ppu2.mdr; } @@ -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; } @@ -203,16 +202,15 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void { //OAMADDH case 0x2103: { io.oamBaseAddress = data.bit(0) << 9 | (io.oamBaseAddress & 0x01fe); - io.oamPriority = data.bit(7); + io.oamPriority = data.bit(7); obj.addressReset(); return; } //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); @@ -651,7 +649,7 @@ auto PPU::updateVideoMode() -> void { memory::assign(bg1.io.priority, 5, 8); memory::assign(bg2.io.priority, 4, 7); memory::assign(bg3.io.priority, 1, 10); - memory::assign(obj.io.priority, 2, 3, 6, 9); + memory::assign(obj.io.priority, 2, 3, 6, 9); } else { memory::assign(bg1.io.priority, 6, 9); memory::assign(bg2.io.priority, 5, 8); diff --git a/higan/sfc/ppu/background/mode7.cpp b/higan/sfc/ppu/mode7.cpp similarity index 100% rename from higan/sfc/ppu/background/mode7.cpp rename to higan/sfc/ppu/mode7.cpp diff --git a/higan/sfc/ppu/object/oam.cpp b/higan/sfc/ppu/oam.cpp similarity index 100% rename from higan/sfc/ppu/object/oam.cpp rename to higan/sfc/ppu/oam.cpp diff --git a/higan/sfc/ppu/object/object.cpp b/higan/sfc/ppu/object.cpp similarity index 100% rename from higan/sfc/ppu/object/object.cpp rename to higan/sfc/ppu/object.cpp diff --git a/higan/sfc/ppu/object/object.hpp b/higan/sfc/ppu/object.hpp similarity index 100% rename from higan/sfc/ppu/object/object.hpp rename to higan/sfc/ppu/object.hpp diff --git a/higan/sfc/ppu/ppu.cpp b/higan/sfc/ppu/ppu.cpp index 82c3457f..3f4f4cb5 100644 --- a/higan/sfc/ppu/ppu.cpp +++ b/higan/sfc/ppu/ppu.cpp @@ -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" diff --git a/higan/sfc/ppu/ppu.hpp b/higan/sfc/ppu/ppu.hpp index e9729910..ff3cb855 100644 --- a/higan/sfc/ppu/ppu.hpp +++ b/higan/sfc/ppu/ppu.hpp @@ -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; diff --git a/higan/sfc/ppu/screen/screen.cpp b/higan/sfc/ppu/screen.cpp similarity index 100% rename from higan/sfc/ppu/screen/screen.cpp rename to higan/sfc/ppu/screen.cpp diff --git a/higan/sfc/ppu/screen/screen.hpp b/higan/sfc/ppu/screen.hpp similarity index 100% rename from higan/sfc/ppu/screen/screen.hpp rename to higan/sfc/ppu/screen.hpp diff --git a/higan/sfc/ppu/window/window.cpp b/higan/sfc/ppu/window.cpp similarity index 100% rename from higan/sfc/ppu/window/window.cpp rename to higan/sfc/ppu/window.cpp diff --git a/higan/sfc/ppu/window/window.hpp b/higan/sfc/ppu/window.hpp similarity index 100% rename from higan/sfc/ppu/window/window.hpp rename to higan/sfc/ppu/window.hpp