From 570eb9c5f5e2cdc001294b7854fbe1350086d47f Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 1 Mar 2016 23:23:18 +1100 Subject: [PATCH] Update to v097r20 release. byuu says: Changelog: - WS: fixed a major CPU bug where I was using the wrong bits for ModR/M's memory mode - WS: added grayscale PPU emulation (exceptionally buggy) GunPey now runs, as long as you add: eeprom name=save.ram size=0x800 to the manifest after importing with icarus. Right now, you can't control the game due to missing keypad polling. There's also a lot of glitchiness with the sprites. Seems like they're not getting properly cleared sometimes or something. Also, the PPU emulation is totally unrealistic bullshit. I decode and evaluate every single tile and sprite on every single pixel of output. No way in hell the hardware could ever come close to that. The speed's around 500fps without the insane sprite evaluations, and around 90fps with it. Obviously, I'll fix this in time. Nothing else seems to run that I've tried. Not even far enough to display any output whatsoever. Tried Langrisser Millenium, Rockman & Forte and Riviera. I really need to update icarus to try and encode eeprom/sram sizes, because that's going to break a lot of stuff if it's missing. --- higan/emulator/emulator.hpp | 2 +- higan/processor/v30mz/modrm.cpp | 15 ++-- higan/ws/cpu/interrupt.cpp | 3 +- higan/ws/memory/memory.cpp | 4 +- higan/ws/ppu/io.cpp | 8 +-- higan/ws/ppu/ppu.cpp | 17 ++++- higan/ws/ppu/ppu.hpp | 17 ++++- higan/ws/ppu/render.cpp | 121 ++++++++++++++++++++++++++++++++ higan/ws/ppu/video.cpp | 12 ++-- 9 files changed, 172 insertions(+), 27 deletions(-) create mode 100644 higan/ws/ppu/render.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 22629d61..a1b8676e 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -6,7 +6,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "097.19"; + static const string Version = "097.20"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/processor/v30mz/modrm.cpp b/higan/processor/v30mz/modrm.cpp index 06b3bb3b..9e825b04 100644 --- a/higan/processor/v30mz/modrm.cpp +++ b/higan/processor/v30mz/modrm.cpp @@ -1,19 +1,14 @@ -//ModRM functions -//d7-d6 => mod -//d5-d3 => reg -//d2-d0 => mem - auto V30MZ::modRM() -> void { - auto byte = fetch(); - modrm.mod = byte >> 6; - modrm.reg = byte >> 3; - modrm.mem = byte >> 0; + auto data = fetch(); + modrm.mem = data.bits(0,2); + modrm.reg = data.bits(3,5); + modrm.mod = data.bits(6,7); if(modrm.mod == 0 && modrm.mem == 6) { modrm.segment = segment(r.ds); modrm.address = fetch(Word); } else { - switch(modrm.reg) { + switch(modrm.mem) { case 0: modrm.segment = segment(r.ds); modrm.address = r.bx + r.si; break; case 1: modrm.segment = segment(r.ds); modrm.address = r.bx + r.di; break; case 2: modrm.segment = segment(r.ss); modrm.address = r.bp + r.si; break; diff --git a/higan/ws/cpu/interrupt.cpp b/higan/ws/cpu/interrupt.cpp index a5db055e..d0cc3b61 100644 --- a/higan/ws/cpu/interrupt.cpp +++ b/higan/ws/cpu/interrupt.cpp @@ -3,9 +3,10 @@ auto CPU::poll() -> void { state.halt = false; if(!V30MZ::r.f.i) return; + //find and execute first pending interrupt in order of priority (7-0) for(int n = 7; n >= 0; n--) { if(r.interruptStatus & r.interruptEnable & (1 << n)) { - interrupt(r.interruptBase + n); + return interrupt(r.interruptBase + n); } } } diff --git a/higan/ws/memory/memory.cpp b/higan/ws/memory/memory.cpp index cd2ad309..271ca68d 100644 --- a/higan/ws/memory/memory.cpp +++ b/higan/ws/memory/memory.cpp @@ -12,12 +12,12 @@ auto IO::power() -> void { } auto IO::portRead(uint16 addr) -> uint8 { - print("[", hex(addr, 4L), "]: port unmapped\n"); +//print("[", hex(addr, 4L), "]: port unmapped\n"); return 0x00; } auto IO::portWrite(uint16 addr, uint8 data) -> void { - print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n"); +//print("[", hex(addr, 4L), "] = ", hex(data, 2L), ": port unmapped\n"); } auto Bus::read(uint20 addr) -> uint8 { diff --git a/higan/ws/ppu/io.cpp b/higan/ws/ppu/io.cpp index a0262e21..5be47943 100644 --- a/higan/ws/ppu/io.cpp +++ b/higan/ws/ppu/io.cpp @@ -3,7 +3,7 @@ auto PPU::portRead(uint16 addr) -> uint8 { if(addr == 0x0000) { return ( r.screenTwoWindowEnable << 5 - | r.screenTwoWindowMode << 4 + | r.screenTwoWindowInvert << 4 | r.spriteWindowEnable << 3 | r.spriteEnable << 2 | r.screenTwoEnable << 1 @@ -122,7 +122,7 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //DISP_CTRL if(addr == 0x0000) { r.screenTwoWindowEnable = data.bit(5); - r.screenTwoWindowMode = data.bit(4); + r.screenTwoWindowInvert = data.bit(4); r.spriteWindowEnable = data.bit(3); r.spriteEnable = data.bit(2); r.screenTwoEnable = data.bit(1); @@ -292,8 +292,8 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //PALMONO if(addr >= 0x0020 && addr <= 0x003f) { - r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 1] = data.bits(6,4); - r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0); + r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 1] = data.bits(6,4); + r.palette[addr.bits(4,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0); return; } diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index 7c92aad7..8edb79eb 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -4,6 +4,7 @@ namespace WonderSwan { PPU ppu; #include "io.cpp" +#include "render.cpp" #include "video.cpp" auto PPU::Enter() -> void { @@ -11,7 +12,21 @@ auto PPU::Enter() -> void { } auto PPU::main() -> void { - step(256); + if(status.vclk < 144) { + for(uint x = 0; x < 224; x++) { + pixel = {Pixel::Source::None, 0xfff}; + renderScreenOne(); + renderScreenTwo(); + renderSprite(); + output[status.vclk * 224 + status.hclk] = pixel.color; + step(1); + } + for(uint x = 224; x < 256; x++) { + step(1); + } + } else { + step(256); + } scanline(); } diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 5dd77eb2..fe63c058 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -8,20 +8,33 @@ struct PPU : Thread, IO { auto step(uint clocks) -> void; auto power() -> void; + //io.cpp auto portRead(uint16 addr) -> uint8 override; auto portWrite(uint16 addr, uint8 data) -> void override; - uint16 output[224 * 144]; + //render.cpp + auto renderScreenOne() -> void; + auto renderScreenTwo() -> void; + auto renderSprite() -> void; + + //state + uint12 output[224 * 144]; struct Status { uint vclk; uint hclk; } status; + struct Pixel { + enum class Source : uint { None, ScreenOne, ScreenTwo, Sprite }; + Source source; + uint12 color; + } pixel; + struct Registers { //$0000 DISP_CTRL bool screenTwoWindowEnable; - bool screenTwoWindowMode; + bool screenTwoWindowInvert; bool spriteWindowEnable; bool spriteEnable; bool screenTwoEnable; diff --git a/higan/ws/ppu/render.cpp b/higan/ws/ppu/render.cpp new file mode 100644 index 00000000..24c57182 --- /dev/null +++ b/higan/ws/ppu/render.cpp @@ -0,0 +1,121 @@ +auto PPU::renderScreenOne() -> void { + if(!r.screenOneEnable) return; + + uint8 scrollX = status.hclk + r.scrollOneX; + uint8 scrollY = status.vclk + r.scrollOneY; + + uint14 tilemapOffset = r.screenOneMapBase << 11; + tilemapOffset += (scrollY >> 3) << 6; + tilemapOffset += (scrollX >> 3) << 1; + + uint16 tile; + tile.byte(0) = iram[tilemapOffset++]; + tile.byte(1) = iram[tilemapOffset++]; + + uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0); + uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0); + + uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4) + (tileY << 1); + uint8 d0 = iram[tileOffset++]; + uint8 d1 = iram[tileOffset++]; + + uint8 tileMask = 0x80 >> tileX; + uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0); + + uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor]; + uint4 poolColor = 15 - r.pool[paletteColor]; + + pixel = {Pixel::Source::ScreenOne, poolColor << 0 | poolColor << 4 | poolColor << 8}; +} + +auto PPU::renderScreenTwo() -> void { + if(!r.screenTwoEnable) return; + + bool windowInside = ( + status.hclk >= r.screenTwoWindowX0 + && status.hclk <= r.screenTwoWindowX1 + && status.vclk >= r.screenTwoWindowY0 + && status.vclk <= r.screenTwoWindowY1 + ); + windowInside ^= r.screenTwoWindowInvert; + if(r.screenTwoWindowEnable && !windowInside) return; + + uint8 scrollX = status.hclk + r.scrollTwoX; + uint8 scrollY = status.vclk + r.scrollTwoY; + + uint14 tilemapOffset = r.screenTwoMapBase << 11; + tilemapOffset += (scrollY >> 3) << 6; + tilemapOffset += (scrollX >> 3) << 1; + + uint16 tile; + tile.byte(0) = iram[tilemapOffset++]; + tile.byte(1) = iram[tilemapOffset++]; + + uint3 tileX = (scrollX & 7) ^ (tile.bit(14) ? 7 : 0); + uint3 tileY = (scrollY & 7) ^ (tile.bit(15) ? 7 : 0); + + uint14 tileOffset = 0x2000 + (tile.bits(0,8) << 4) + (tileY << 1); + uint8 d0 = iram[tileOffset++]; + uint8 d1 = iram[tileOffset++]; + + uint8 tileMask = 0x80 >> tileX; + uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0); + + if(tile.bit(11) && tileColor == 0) return; + uint3 paletteColor = r.palette[tile.bits(9,12)].color[tileColor]; + uint4 poolColor = 15 - r.pool[paletteColor]; + + pixel = {Pixel::Source::ScreenTwo, poolColor << 0 | poolColor << 4 | poolColor << 8}; +} + +auto PPU::renderSprite() -> void { + if(!r.spriteEnable) return; + + bool windowInside = ( + status.hclk >= r.spriteWindowX0 + && status.hclk <= r.spriteWindowX1 + && status.vclk >= r.spriteWindowY0 + && status.vclk <= r.spriteWindowY1 + ); + + uint14 spriteBase = r.spriteBase << 9; + + uint7 spriteIndex = r.spriteFirst; + uint8 spriteCount = max(128, (uint)r.spriteCount); + while(spriteCount--) { + uint32 sprite; + sprite.byte(0) = iram[spriteBase + (spriteIndex << 2) + 0]; + sprite.byte(1) = iram[spriteBase + (spriteIndex << 2) + 1]; + sprite.byte(2) = iram[spriteBase + (spriteIndex << 2) + 2]; + sprite.byte(3) = iram[spriteBase + (spriteIndex << 2) + 3]; + spriteIndex++; + + if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue; + if(pixel.source == Pixel::Source::ScreenTwo && !sprite.bit(13)) continue; + + uint8 spriteY = sprite.bits(16,23); + uint8 spriteX = sprite.bits(24,31); + + if(status.hclk < spriteX) continue; + if(status.hclk > (uint8)(spriteX + 7)) continue; + if(status.vclk < spriteY) continue; + if(status.vclk > (uint8)(spriteY + 7)) continue; + + uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0); + uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0); + + uint14 tileOffset = 0x2000 + (sprite.bits(0,8) << 4) + (tileY << 1); + uint8 d0 = iram[tileOffset++]; + uint8 d1 = iram[tileOffset++]; + + uint8 tileMask = 0x80 >> tileX; + uint2 tileColor = (d0 & tileMask ? 1 : 0) | (d1 & tileMask ? 2 : 0); + + if(sprite.bit(11) && tileColor == 0) continue; + uint3 paletteColor = r.palette[8 + sprite.bits(9,11)].color[tileColor]; + uint4 poolColor = 15 - r.pool[paletteColor]; + + pixel = {Pixel::Source::Sprite, poolColor << 0 | poolColor << 4 | poolColor << 8}; + return; + } +} diff --git a/higan/ws/ppu/video.cpp b/higan/ws/ppu/video.cpp index eb9f530d..e0c7e7be 100644 --- a/higan/ws/ppu/video.cpp +++ b/higan/ws/ppu/video.cpp @@ -9,12 +9,12 @@ Video::Video() { auto Video::power() -> void { memory::fill(output(), 224 * 224 * sizeof(uint32)); - for(auto color : range(1 << 12)) { + for(uint12 color : range(1 << 12)) { paletteLiteral[color] = color; - uint R = (uint4)(color >> 8); - uint G = (uint4)(color >> 4); - uint B = (uint4)(color >> 0); + uint B = color.bits(0, 3); + uint G = color.bits(4, 7); + uint R = color.bits(8,11); R = image::normalize(R, 4, 16); G = image::normalize(G, 4, 16); @@ -26,10 +26,10 @@ auto Video::power() -> void { auto Video::refresh() -> void { for(uint y = 0; y < 144; y++) { auto source = ppu.output + y * 224; - auto target = output() + y * 224; for(uint x = 0; x < 224; x++) { auto color = paletteStandard[*source++]; - *target++ = color; + //*(output() + y * 224 + x) = color; + *(output() + (223 - x) * 224 + 40 + y) = color; } }