From c33065fbd1601f58a464dbb5dad5f7ffd9463bb3 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 13 Mar 2016 00:27:41 +1100 Subject: [PATCH] Update to v097r24 release. byuu says: Changelog: - WS: fixed bug when IRQs triggered during a rep string instruction - WS: added sprite attribute caching (per-scanline); absolutely massive speed-up - WS: emulated limit of 32 sprites per scanline - WS: emulated the extended PPU register bit behavior based on the DISP_CTRL tile bit-depth setting - WS: added "Rotate" key binding; can be used to flip the WS display between horizontal and vertical in real-time The prefix emulation may not be 100% hardware-accurate, but the edge cases should be extreme enough to not come up in the WS library. No way to get the emulation 100% down without intensive hardware testing. trap15 pointed me at a workflow diagram for it, but that diagram is impossible without a magic internal stack frame that grows with every IRQ, and can thus grow infinitely large. The rotation thing isn't exactly the most friendly set-up, but oh well. I'll see about adding a default rotation setting to manifests, so that games like GunPey can start in the correct orientation. After that, if the LCD orientation icon turns out to be reliable, then I'll start using that. But if there are cases where it's not reliable, then I'll leave it to manual button presses. Speaking of icons, I'll need a set of icons to render on the screen. Going to put them to the top right on vertical orientation, and on the bottom left for horizontal orientation. Just outside of the video output, of course. Overall, WS is getting pretty far along, but still some major bugs in various games. I really need sound emulation, though. Nobody's going to use this at all without that. --- higan/emulator/emulator.hpp | 2 +- higan/processor/v30mz/instructions-misc.cpp | 9 +- higan/processor/v30mz/instructions-string.cpp | 78 ++++++++----- higan/processor/v30mz/registers.cpp | 15 ++- higan/processor/v30mz/v30mz.cpp | 105 +++++++++--------- higan/processor/v30mz/v30mz.hpp | 22 ++-- higan/ws/cpu/cpu.cpp | 2 +- higan/ws/cpu/io.cpp | 32 +++--- higan/ws/interface/interface.cpp | 16 +-- higan/ws/ppu/io.cpp | 45 ++++---- higan/ws/ppu/ppu.cpp | 2 + higan/ws/ppu/ppu.hpp | 15 +++ higan/ws/ppu/render-color.cpp | 34 ++---- higan/ws/ppu/render-mono.cpp | 40 +++---- higan/ws/ppu/render-sprite.cpp | 31 ++++++ higan/ws/ppu/video.cpp | 32 +++++- higan/ws/system/system.cpp | 21 ++++ higan/ws/system/system.hpp | 22 ++-- higan/ws/ws.hpp | 14 ++- 19 files changed, 324 insertions(+), 213 deletions(-) create mode 100644 higan/ws/ppu/render-sprite.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index cf1fa004..ff693915 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.23"; + static const string Version = "097.24"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/processor/v30mz/instructions-misc.cpp b/higan/processor/v30mz/instructions-misc.cpp index 0fa4c105..043a99a6 100644 --- a/higan/processor/v30mz/instructions-misc.cpp +++ b/higan/processor/v30mz/instructions-misc.cpp @@ -3,7 +3,8 @@ //36 ss: //3e ds: auto V30MZ::opSegment(uint16 segment) { - prefix.segment = segment; + if(prefixes.size() >= 7) prefixes.removeLast(); + prefixes.prepend(opcode); state.prefix = true; state.poll = false; } @@ -11,15 +12,17 @@ auto V30MZ::opSegment(uint16 segment) { //f2 repnz: //f3 repz: auto V30MZ::opRepeat(bool flag) { + if(prefixes.size() >= 7) prefixes.removeLast(); + prefixes.prepend(opcode); wait(4); - if(r.cx == 0) return; - prefix.repeat = flag; state.prefix = true; state.poll = false; } //f0 lock: auto V30MZ::opLock() { + if(prefixes.size() >= 7) prefixes.removeLast(); + prefixes.prepend(opcode); state.prefix = true; state.poll = false; } diff --git a/higan/processor/v30mz/instructions-string.cpp b/higan/processor/v30mz/instructions-string.cpp index df785a08..58343e27 100644 --- a/higan/processor/v30mz/instructions-string.cpp +++ b/higan/processor/v30mz/instructions-string.cpp @@ -1,10 +1,12 @@ auto V30MZ::opInString(Size size) { wait(5); - auto data = in(size, r.dx); - write(size, r.es, r.di, data); - r.di += r.f.d ? -size : size; + if(!repeat() || r.cx) { + auto data = in(size, r.dx); + write(size, r.es, r.di, data); + r.di += r.f.d ? -size : size; + + if(!repeat() || !--r.cx) return; - if(prefix.repeat && --r.cx) { state.prefix = true; r.ip--; } @@ -12,11 +14,13 @@ auto V30MZ::opInString(Size size) { auto V30MZ::opOutString(Size size) { wait(6); - auto data = read(size, segment(r.ds), r.si); - out(size, r.dx, data); - r.si += r.f.d ? -size : size; + if(!repeat() || r.cx) { + auto data = read(size, segment(r.ds), r.si); + out(size, r.dx, data); + r.si += r.f.d ? -size : size; + + if(!repeat() || !--r.cx) return; - if(prefix.repeat && --r.cx) { state.prefix = true; r.ip--; } @@ -24,12 +28,14 @@ auto V30MZ::opOutString(Size size) { auto V30MZ::opMoveString(Size size) { wait(4); - auto data = read(size, segment(r.ds), r.si); - write(size, r.es, r.di, data); - r.si += r.f.d ? -size : size; - r.di += r.f.d ? -size : size; + if(!repeat() || r.cx) { + auto data = read(size, segment(r.ds), r.si); + write(size, r.es, r.di, data); + r.si += r.f.d ? -size : size; + r.di += r.f.d ? -size : size; + + if(!repeat() || !--r.cx) return; - if(prefix.repeat && --r.cx) { state.prefix = true; r.ip--; } @@ -37,13 +43,17 @@ auto V30MZ::opMoveString(Size size) { auto V30MZ::opCompareString(Size size) { wait(5); - auto x = read(size, segment(r.ds), r.si); - auto y = read(size, r.es, r.di); - r.si += r.f.d ? -size : size; - r.di += r.f.d ? -size : size; - alSub(size, x, y); + if(!repeat() || r.cx) { + auto x = read(size, segment(r.ds), r.si); + auto y = read(size, r.es, r.di); + r.si += r.f.d ? -size : size; + r.di += r.f.d ? -size : size; + alSub(size, x, y); + + if(!repeat() || !--r.cx) return; + if(repeat() == RepeatWhileZero && r.f.z == 1) return; + if(repeat() == RepeatWhileNotZero && r.f.z == 0) return; - if(prefix.repeat && prefix.repeat() == r.f.z && --r.cx) { state.prefix = true; r.ip--; } @@ -51,10 +61,12 @@ auto V30MZ::opCompareString(Size size) { auto V30MZ::opStoreString(Size size) { wait(2); - write(size, r.es, r.di, getAcc(size)); - r.di += r.f.d ? -size : size; + if(!repeat() || r.cx) { + write(size, r.es, r.di, getAcc(size)); + r.di += r.f.d ? -size : size; + + if(!repeat() || !--r.cx) return; - if(prefix.repeat && --r.cx) { state.prefix = true; r.ip--; } @@ -64,10 +76,12 @@ auto V30MZ::opStoreString(Size size) { //ad lodsw auto V30MZ::opLoadString(Size size) { wait(2); - setAcc(size, read(size, segment(r.ds), r.si)); - r.si += r.f.d ? -size : size; + if(!repeat() || r.cx) { + setAcc(size, read(size, segment(r.ds), r.si)); + r.si += r.f.d ? -size : size; + + if(!repeat() || !--r.cx) return; - if(prefix.repeat && --r.cx) { state.prefix = true; r.ip--; } @@ -77,12 +91,16 @@ auto V30MZ::opLoadString(Size size) { //af scasw auto V30MZ::opScanString(Size size) { wait(3); - auto x = getAcc(size); - auto y = read(size, r.es, r.di); - r.di += r.f.d ? -size : size; - alSub(size, x, y); + if(!repeat() || r.cx) { + auto x = getAcc(size); + auto y = read(size, r.es, r.di); + r.di += r.f.d ? -size : size; + alSub(size, x, y); + + if(!repeat() || !--r.cx) return; + if(repeat() == RepeatWhileZero && r.f.z == 1) return; + if(repeat() == RepeatWhileNotZero && r.f.z == 0) return; - if(prefix.repeat && prefix.repeat() == r.f.z && --r.cx) { state.prefix = true; r.ip--; } diff --git a/higan/processor/v30mz/registers.cpp b/higan/processor/v30mz/registers.cpp index aafe5aca..02e33a57 100644 --- a/higan/processor/v30mz/registers.cpp +++ b/higan/processor/v30mz/registers.cpp @@ -1,5 +1,18 @@ +auto V30MZ::repeat() -> uint8 { + for(auto prefix : prefixes) { + if(prefix == RepeatWhileZero) return prefix; + if(prefix == RepeatWhileNotZero) return prefix; + } + return {}; +} + auto V30MZ::segment(uint16 segment) -> uint16 { - if(prefix.segment) return prefix.segment(); + for(auto prefix : prefixes) { + if(prefix == SegmentOverrideES) return r.es; + if(prefix == SegmentOverrideCS) return r.cs; + if(prefix == SegmentOverrideSS) return r.ss; + if(prefix == SegmentOverrideDS) return r.ds; + } return segment; } diff --git a/higan/processor/v30mz/v30mz.cpp b/higan/processor/v30mz/v30mz.cpp index d5d8f74d..d784e59c 100644 --- a/higan/processor/v30mz/v30mz.cpp +++ b/higan/processor/v30mz/v30mz.cpp @@ -21,28 +21,67 @@ auto V30MZ::debug(string text) -> void { print(text, "\n"); } +auto V30MZ::power() -> void { + state.halt = false; + state.poll = true; + state.prefix = false; + prefixes.reset(); + + r.ax = 0x0000; + r.cx = 0x0000; + r.dx = 0x0000; + r.bx = 0x0000; + r.sp = 0x0000; + r.bp = 0x0000; + r.si = 0x0000; + r.di = 0x0000; + r.es = 0x0000; + r.cs = 0xffff; + r.ss = 0x0000; + r.ds = 0x0000; + r.ip = 0x0000; + r.f = 0x8000; +} + auto V30MZ::exec() -> void { state.poll = true; + state.prefix = false; if(state.halt) return wait(1); - #if 0 - static uint counter = 0; - static uint16 cs = 0, ip = 0; - if(cs != r.cs || ip != r.ip) print(disassemble(cs = r.cs, ip = r.ip), "\n"); - #endif - instruction(); + if(!state.prefix) prefixes.reset(); +} - if(state.prefix) { - state.prefix = false; - } else { - prefix.repeat = nothing; - prefix.segment = nothing; +auto V30MZ::interrupt(uint8 vector) -> void { + state.halt = false; + state.poll = true; + state.prefix = false; + + //if an IRQ fires during a rep string instruction; + //flush prefix queue and seek back to first prefix. + //this allows the transfer to resume after the IRQ. + if(prefixes) { + r.ip -= prefixes.size(); + prefixes.reset(); } + + auto ip = read(Word, 0x0000, vector * 4 + 0); + auto cs = read(Word, 0x0000, vector * 4 + 2); + + push(r.f); + push(r.cs); + push(r.ip); + + r.f.m = true; + r.f.i = false; + r.f.b = false; + + r.ip = ip; + r.cs = cs; } auto V30MZ::instruction() -> void { - auto opcode = fetch(); + opcode = fetch(); wait(1); switch(opcode) { @@ -305,46 +344,4 @@ auto V30MZ::instruction() -> void { } } -auto V30MZ::interrupt(uint8 vector) -> void { - state.halt = false; - - auto ip = read(Word, 0x0000, vector * 4 + 0); - auto cs = read(Word, 0x0000, vector * 4 + 2); - - push(r.f); - push(r.cs); - push(r.ip); - - r.f.m = true; - r.f.i = false; - r.f.b = false; - - r.ip = ip; - r.cs = cs; -} - -auto V30MZ::power() -> void { - state.halt = false; - state.poll = true; - state.prefix = false; - - prefix.repeat = nothing; - prefix.segment = nothing; - - r.ax = 0x0000; - r.cx = 0x0000; - r.dx = 0x0000; - r.bx = 0x0000; - r.sp = 0x0000; - r.bp = 0x0000; - r.si = 0x0000; - r.di = 0x0000; - r.es = 0x0000; - r.cs = 0xffff; - r.ss = 0x0000; - r.ds = 0x0000; - r.ip = 0x0000; - r.f = 0x8000; -} - } diff --git a/higan/processor/v30mz/v30mz.hpp b/higan/processor/v30mz/v30mz.hpp index 23694a70..16555377 100644 --- a/higan/processor/v30mz/v30mz.hpp +++ b/higan/processor/v30mz/v30mz.hpp @@ -7,6 +7,15 @@ namespace Processor { struct V30MZ { using Size = uint; enum : uint { Byte = 1, Word = 2, Long = 4 }; + enum : uint { + SegmentOverrideES = 0x26, + SegmentOverrideCS = 0x2e, + SegmentOverrideSS = 0x36, + SegmentOverrideDS = 0x3e, + Lock = 0xf0, + RepeatWhileNotZero = 0xf2, + RepeatWhileZero = 0xf3, + }; virtual auto wait(uint clocks = 1) -> void = 0; virtual auto read(uint20 addr) -> uint8 = 0; @@ -15,12 +24,13 @@ struct V30MZ { virtual auto out(uint16 port, uint8 data) -> void = 0; auto debug(string text) -> void; - auto exec() -> void; - auto instruction() -> void; - auto interrupt(uint8 vector) -> void; auto power() -> void; + auto exec() -> void; + auto interrupt(uint8 vector) -> void; + auto instruction() -> void; //registers.cpp + auto repeat() -> uint8; auto segment(uint16) -> uint16; auto getAcc(Size) -> uint32; @@ -202,10 +212,8 @@ struct V30MZ { bool prefix; //set to true for prefix instructions; prevents flushing of Prefix struct } state; - struct Prefix { - maybe repeat; //repnz, repz - maybe segment; //cs, es, ss, ds - } prefix; + uint8 opcode; + vector prefixes; struct ModRM { uint2 mod; diff --git a/higan/ws/cpu/cpu.cpp b/higan/ws/cpu/cpu.cpp index 5f5c00d7..ec1045e2 100644 --- a/higan/ws/cpu/cpu.cpp +++ b/higan/ws/cpu/cpu.cpp @@ -55,7 +55,7 @@ auto CPU::power() -> void { iomap[0x00b5] = this; iomap[0x00b6] = this; - if(WSC() || SC()) { + if(system.model() != Model::WonderSwan) { for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this; iomap[0x0062] = this; } diff --git a/higan/ws/cpu/io.cpp b/higan/ws/cpu/io.cpp index 21df93c8..7eb75335 100644 --- a/higan/ws/cpu/io.cpp +++ b/higan/ws/cpu/io.cpp @@ -1,25 +1,24 @@ auto CPU::keypadRead() -> uint4 { - uint1 orientation = 0; uint4 data = 0; if(r.ypadEnable) { - data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y1) << 0; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y2) << 1; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y3) << 2; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y4) << 3; + data |= system.keypad.y1 << 0; + data |= system.keypad.y2 << 1; + data |= system.keypad.y3 << 2; + data |= system.keypad.y4 << 3; } if(r.xpadEnable) { - data |= interface->inputPoll(orientation, 0, (uint)Keypad::X1) << 0; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::X2) << 1; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::X3) << 2; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::X4) << 3; + data |= system.keypad.x1 << 0; + data |= system.keypad.x2 << 1; + data |= system.keypad.x3 << 2; + data |= system.keypad.x4 << 3; } if(r.buttonEnable) { - data |= interface->inputPoll(orientation, 0, (uint)Keypad::Start) << 1; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::A) << 2; - data |= interface->inputPoll(orientation, 0, (uint)Keypad::B) << 3; + data |= system.keypad.start << 1; + data |= system.keypad.a << 2; + data |= system.keypad.b << 3; } return data; @@ -44,22 +43,23 @@ auto CPU::portRead(uint16 addr) -> uint8 { //WSC_SYSTEM if(addr == 0x0062) { - return SC() << 7; + return (system.model() == Model::SwanCrystal) << 7; } //HW_FLAGS if(addr == 0x00a0) { + bool model = system.model() != Model::WonderSwan; return ( 1 << 7 //1 = built-in self-test passed | 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width - | !WS() << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal + | model << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal | 1 << 0 //0 = BIOS mapped; 1 = cartridge mapped ); } //INT_BASE if(addr == 0x00b0) { - if(WS()) return r.interruptBase | 3; + if(system.model() == Model::WonderSwan) return r.interruptBase | 3; return r.interruptBase; } @@ -116,7 +116,7 @@ auto CPU::portWrite(uint16 addr, uint8 data) -> void { //INT_BASE if(addr == 0x00b0) { - r.interruptBase = WS() ? data & ~7 : data & ~1; + r.interruptBase = (system.model() == Model::WonderSwan) ? data & ~7 : data & ~1; return; } diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index 2cbfed77..0c742be2 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -34,7 +34,8 @@ Interface::Interface() { device.input.append({ 8, 0, "B"}); device.input.append({ 9, 0, "A"}); device.input.append({10, 0, "Start"}); - device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + device.input.append({11, 0, "Rotate"}); + device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; this->device.append(device); } @@ -50,7 +51,8 @@ Interface::Interface() { device.input.append({ 8, 0, "B"}); device.input.append({ 9, 0, "A"}); device.input.append({10, 0, "Start"}); - device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + device.input.append({11, 0, "Rotate"}); + device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; this->device.append(device); } @@ -93,10 +95,10 @@ auto Interface::group(uint id) -> uint { case ID::RAM: case ID::EEPROM: switch(system.model()) { - case System::Model::WonderSwan: + case Model::WonderSwan: return ID::WonderSwan; - case System::Model::WonderSwanColor: - case System::Model::SwanCrystal: + case Model::WonderSwanColor: + case Model::SwanCrystal: return ID::WonderSwanColor; } } @@ -104,8 +106,8 @@ auto Interface::group(uint id) -> uint { } auto Interface::load(uint id) -> void { - if(id == ID::WonderSwan) system.load(System::Model::WonderSwan); - if(id == ID::WonderSwanColor) system.load(System::Model::WonderSwanColor); + if(id == ID::WonderSwan) system.load(Model::WonderSwan); + if(id == ID::WonderSwanColor) system.load(Model::WonderSwanColor); } auto Interface::save() -> void { diff --git a/higan/ws/ppu/io.cpp b/higan/ws/ppu/io.cpp index f12fba81..596d1515 100644 --- a/higan/ws/ppu/io.cpp +++ b/higan/ws/ppu/io.cpp @@ -12,7 +12,13 @@ auto PPU::portRead(uint16 addr) -> uint8 { } //BACK_COLOR - if(addr == 0x0001) return r.backColor; + if(addr == 0x0001) { + if(!system.depth()) { + return r.backColor.bits(0,2); + } else { + return r.backColor.bits(0,7); + } + } //LINE_CUR if(addr == 0x0002) return status.vclk; @@ -21,7 +27,13 @@ auto PPU::portRead(uint16 addr) -> uint8 { if(addr == 0x0003) return r.lineCompare; //SPR_BASE - if(addr == 0x0004) return r.spriteBase; + if(addr == 0x0004) { + if(!system.depth()) { + return r.spriteBase.bits(0,4); + } else { + return r.spriteBase.bits(0,5); + } + } //SPR_FIRST if(addr == 0x0005) return r.spriteFirst; @@ -30,7 +42,13 @@ auto PPU::portRead(uint16 addr) -> uint8 { if(addr == 0x0006) return r.spriteCount; //MAP_BASE - if(addr == 0x0007) return r.screenTwoMapBase << 4 | r.screenOneMapBase << 0; + if(addr == 0x0007) { + if(!system.depth()) { + return r.screenTwoMapBase.bits(0,2) << 4 | r.screenOneMapBase.bits(0,2) << 0; + } else { + return r.screenTwoMapBase.bits(0,3) << 4 | r.screenOneMapBase.bits(0,3) << 0; + } + } //SCR2_WIN_X0 if(addr == 0x0008) return r.screenTwoWindowX0; @@ -122,11 +140,7 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //BACK_COLOR if(addr == 0x0001) { - if(WS()) { - r.backColor = data.bits(0,2); - } else { - r.backColor = data.bits(0,7); - } + r.backColor = data; return; } @@ -138,11 +152,7 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //SPR_BASE if(addr == 0x0004) { - if(WS()) { - r.spriteBase = data.bits(4,0); - } else { - r.spriteBase = data.bits(5,0); - } + r.spriteBase = data.bits(0,5); return; } @@ -160,13 +170,8 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //MAP_BASE if(addr == 0x0007) { - if(WS()) { - r.screenTwoMapBase = data.bits(6,4); - r.screenOneMapBase = data.bits(2,0); - } else { - r.screenTwoMapBase = data.bits(7,4); - r.screenOneMapBase = data.bits(3,0); - } + r.screenOneMapBase = data.bits(0,3); + r.screenTwoMapBase = data.bits(4,7); return; } diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index 7dff785e..fbd28f5b 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-sprite.cpp" #include "render-mono.cpp" #include "render-color.cpp" #include "video.cpp" @@ -14,6 +15,7 @@ auto PPU::Enter() -> void { auto PPU::main() -> void { if(status.vclk < 144) { + renderSpriteDecode(); for(uint x = 0; x < 224; x++) { if(!system.color()) { renderMonoBack(); diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index fd468301..67804d06 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -12,6 +12,9 @@ struct PPU : Thread, IO { auto portRead(uint16 addr) -> uint8 override; auto portWrite(uint16 addr, uint8 data) -> void override; + //render-sprite.cpp + auto renderSpriteDecode() -> void; + //render-mono.cpp auto renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2; auto renderMonoBack() -> void; @@ -34,6 +37,18 @@ struct PPU : Thread, IO { uint hclk; } status; + struct Sprite { + uint8 x; + uint8 y; + uint1 vflip; + uint1 hflip; + uint1 priority; + uint1 window; + uint4 palette; //renderSpriteDecode() always sets bit3 + uint9 tile; + }; + vector sprites; + struct Pixel { enum class Source : uint { Back, ScreenOne, ScreenTwo, Sprite }; Source source; diff --git a/higan/ws/ppu/render-color.cpp b/higan/ws/ppu/render-color.cpp index dc85725d..76212836 100644 --- a/higan/ws/ppu/render-color.cpp +++ b/higan/ws/ppu/render-color.cpp @@ -72,33 +72,21 @@ auto PPU::renderColorScreenTwo() -> void { auto PPU::renderColorSprite() -> void { if(!r.spriteEnable) return; - bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1 - && status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1; - uint16 spriteBase = r.spriteBase << 9; + bool windowInside = status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1; + for(auto& sprite : sprites) { + if(r.spriteWindowEnable && sprite.window && !windowInside) continue; + if(status.hclk < sprite.x) continue; + if(status.hclk > sprite.x + 7) continue; - uint7 spriteIndex = r.spriteFirst; - uint8 spriteCount = min(128, (uint)r.spriteCount); - while(spriteCount--) { - uint32 sprite = iram.read(spriteBase + (spriteIndex++ << 2), Long); - if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue; - - uint8 spriteY = sprite.bits(16,23); - uint8 spriteX = sprite.bits(24,31); - - if(status.vclk < spriteY) continue; - if(status.vclk > (uint8)(spriteY + 7)) continue; - if(status.hclk < spriteX) continue; - if(status.hclk > (uint8)(spriteX + 7)) continue; - - uint16 tileOffset = 0x4000 + (sprite.bits(0,8) << 5); - uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0); - uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0); + uint16 tileOffset = 0x4000 + (sprite.tile << 5); + uint3 tileY = (uint8)(status.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0); + uint3 tileX = (uint8)(status.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0); uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX); if(tileColor == 0) continue; - if(!sprite.bit(13) && pixel.source == Pixel::Source::ScreenTwo) continue; + if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue; - uint12 color = iram.read(0xff00 + (sprite.bits(9,11) << 5) + (tileColor << 1), Word); + uint12 color = iram.read(0xfe00 + (sprite.palette << 5) + (tileColor << 1), Word); pixel = {Pixel::Source::Sprite, color}; - return; + break; } } diff --git a/higan/ws/ppu/render-mono.cpp b/higan/ws/ppu/render-mono.cpp index 6fdb94c5..f5955852 100644 --- a/higan/ws/ppu/render-mono.cpp +++ b/higan/ws/ppu/render-mono.cpp @@ -26,7 +26,7 @@ auto PPU::renderMonoScreenOne() -> void { uint8 scrollY = status.vclk + r.scrollOneY; uint8 scrollX = status.hclk + r.scrollOneX; - uint14 tilemapOffset = r.screenOneMapBase << 11; + uint14 tilemapOffset = r.screenOneMapBase.bits(0,2) << 11; tilemapOffset += (scrollY >> 3) << 6; tilemapOffset += (scrollX >> 3) << 1; @@ -53,7 +53,7 @@ auto PPU::renderMonoScreenTwo() -> void { uint8 scrollX = status.hclk + r.scrollTwoX; uint8 scrollY = status.vclk + r.scrollTwoY; - uint14 tilemapOffset = r.screenTwoMapBase << 11; + uint14 tilemapOffset = r.screenTwoMapBase.bits(0,2) << 11; tilemapOffset += (scrollY >> 3) << 6; tilemapOffset += (scrollX >> 3) << 1; @@ -72,34 +72,22 @@ auto PPU::renderMonoScreenTwo() -> void { auto PPU::renderMonoSprite() -> void { if(!r.spriteEnable) return; - bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1 - && status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1; - uint14 spriteBase = r.spriteBase << 9; + bool windowInside = status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1; + for(auto& sprite : sprites) { + if(r.spriteWindowEnable && sprite.window && !windowInside) continue; + if(status.hclk < sprite.x) continue; + if(status.hclk > sprite.x + 7) continue; - uint7 spriteIndex = r.spriteFirst; - uint8 spriteCount = min(128, (uint)r.spriteCount); - while(spriteCount--) { - uint32 sprite = iram.read(spriteBase + (spriteIndex++ << 2), Long); - if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue; - - uint8 spriteY = sprite.bits(16,23); - uint8 spriteX = sprite.bits(24,31); - - if(status.vclk < spriteY) continue; - if(status.vclk > (uint8)(spriteY + 7)) continue; - if(status.hclk < spriteX) continue; - if(status.hclk > (uint8)(spriteX + 7)) continue; - - uint14 tileOffset = 0x2000 + (sprite.bits(0,8) << 4); - uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0); - uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0); + uint14 tileOffset = 0x2000 + (sprite.tile << 4); + uint3 tileY = (uint8)(status.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0); + uint3 tileX = (uint8)(status.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0); uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX); - if(sprite.bit(11) && tileColor == 0) continue; - if(!sprite.bit(13) && pixel.source == Pixel::Source::ScreenTwo) continue; + if(sprite.palette.bit(2) && tileColor == 0) continue; + if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue; - uint3 paletteColor = r.palette[8 + sprite.bits(9,11)].color[tileColor]; + uint3 paletteColor = r.palette[sprite.palette].color[tileColor]; uint4 poolColor = 15 - r.pool[paletteColor]; pixel = {Pixel::Source::Sprite, poolColor << 0 | poolColor << 4 | poolColor << 8}; - return; + break; } } diff --git a/higan/ws/ppu/render-sprite.cpp b/higan/ws/ppu/render-sprite.cpp new file mode 100644 index 00000000..5c20c333 --- /dev/null +++ b/higan/ws/ppu/render-sprite.cpp @@ -0,0 +1,31 @@ +auto PPU::renderSpriteDecode() -> void { + sprites.reset(); + sprites.reserve(32); + if(!r.spriteEnable) return; + + uint offset = 0; + bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1; + uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9; + uint7 spriteIndex = r.spriteFirst; + uint8 spriteCount = min(128, (uint)r.spriteCount); + while(spriteCount--) { + uint32 attributes = iram.read(spriteBase + (spriteIndex++ << 2), Long); + + Sprite sprite; + sprite.x = attributes.bits(24,31); + if(sprite.x > 224) continue; + sprite.y = attributes.bits(16,23); + if(status.vclk < sprite.y) continue; + if(status.vclk > sprite.y + 7) continue; + sprite.vflip = attributes.bit(15); + sprite.hflip = attributes.bit(14); + sprite.priority = attributes.bit(13); + sprite.window = attributes.bit(12); + if(r.spriteWindowEnable && sprite.window && !windowInside) continue; + sprite.palette = 8 + attributes.bits(9,11); + sprite.tile = attributes.bits(0,8); + + sprites.append(sprite); + if(sprites.size() >= 32) break; + } +} diff --git a/higan/ws/ppu/video.cpp b/higan/ws/ppu/video.cpp index 37930792..68e9b6d7 100644 --- a/higan/ws/ppu/video.cpp +++ b/higan/ws/ppu/video.cpp @@ -24,12 +24,32 @@ auto Video::power() -> void { } auto Video::refresh() -> void { - for(uint y = 0; y < 144; y++) { - auto source = ppu.output + y * 224; - for(uint x = 0; x < 224; x++) { - auto color = paletteStandard[*source++]; - *(output() + (y + 40) * 224 + x) = color; - //*(output() + (223 - x) * 224 + 40 + y) = color; + if(system.orientation() == 0) { + for(uint y = 0; y < 224; y++) { + auto target = output() + y * 224; + if(y < 40 || y >= 184) { + memory::fill(target, 224 * sizeof(uint32)); + continue; + } + auto source = ppu.output + (y - 40) * 224; + for(uint x = 0; x < 224; x++) { + auto color = paletteStandard[*source++]; + *target++ = color; + } + } + } + + if(system.orientation() == 1) { + for(uint y = 0; y < 224; y++) { + auto target = output() + y * 224; + memory::fill(target, 40 * sizeof(uint32)); + memory::fill(target + 184, 40 * sizeof(uint32)); + target += 40; + for(uint x = 0; x < 144; x++) { + auto source = ppu.output + x * 224 + (223 - y); + auto color = paletteStandard[*source]; + *target++ = color; + } } } diff --git a/higan/ws/system/system.cpp b/higan/ws/system/system.cpp index 39944765..f86a303b 100644 --- a/higan/ws/system/system.cpp +++ b/higan/ws/system/system.cpp @@ -7,9 +7,11 @@ System system; auto System::loaded() const -> bool { return _loaded; } auto System::model() const -> Model { return _model; } +auto System::orientation() const -> bool { return _orientation; } auto System::color() const -> bool { return r.color; } auto System::planar() const -> bool { return r.format == 0; } auto System::packed() const -> bool { return r.format == 1; } +auto System::depth() const -> bool { return r.depth == 1; } auto System::init() -> void { } @@ -19,6 +21,7 @@ auto System::term() -> void { auto System::load(Model model) -> void { _model = model; + _orientation = 0; interface->loadRequest(ID::SystemManifest, "manifest.bml", true); auto document = BML::unserialize(information.manifest); @@ -67,6 +70,24 @@ auto System::power() -> void { auto System::run() -> void { while(scheduler.enter() != Scheduler::Event::Frame); + + bool rotate = keypad.rotate; + keypad.y1 = interface->inputPoll(_orientation, 0, 0); + keypad.y2 = interface->inputPoll(_orientation, 0, 1); + keypad.y3 = interface->inputPoll(_orientation, 0, 2); + keypad.y4 = interface->inputPoll(_orientation, 0, 3); + keypad.x1 = interface->inputPoll(_orientation, 0, 4); + keypad.x2 = interface->inputPoll(_orientation, 0, 5); + keypad.x3 = interface->inputPoll(_orientation, 0, 6); + keypad.x4 = interface->inputPoll(_orientation, 0, 7); + keypad.b = interface->inputPoll(_orientation, 0, 8); + keypad.a = interface->inputPoll(_orientation, 0, 9); + keypad.start = interface->inputPoll(_orientation, 0, 10); + keypad.rotate = interface->inputPoll(_orientation, 0, 11); + + if(!rotate && keypad.rotate) { + _orientation = !_orientation; + } } } diff --git a/higan/ws/system/system.hpp b/higan/ws/system/system.hpp index 0eb47137..8c94e3a6 100644 --- a/higan/ws/system/system.hpp +++ b/higan/ws/system/system.hpp @@ -1,21 +1,11 @@ -enum class Keypad : uint { - Y1, Y2, Y3, Y4, - X1, X2, X3, X4, - B, A, Start, -}; - struct System : IO { - enum class Model : uint { - WonderSwan, //SW-001 (ASWAN) - WonderSwanColor, //WSC-001 (SPHINX) - SwanCrystal, //SCT-001 (SPHINX2) - }; - auto loaded() const -> bool; auto model() const -> Model; + auto orientation() const -> bool; auto color() const -> bool; auto planar() const -> bool; auto packed() const -> bool; + auto depth() const -> bool; auto init() -> void; auto term() -> void; @@ -33,6 +23,13 @@ struct System : IO { EEPROM eeprom; + struct Keypad { + bool y1, y2, y3, y4; + bool x1, x2, x3, x4; + bool b, a, start; + bool rotate; + } keypad; + struct Registers { //$0060 DISP_MODE uint1 depth; @@ -44,6 +41,7 @@ struct System : IO { privileged: bool _loaded = false; Model _model = Model::WonderSwan; + bool _orientation = 0; //0 = horizontal, 1 = vertical }; extern System system; diff --git a/higan/ws/ws.hpp b/higan/ws/ws.hpp index d8e1f8d5..1e3f7260 100644 --- a/higan/ws/ws.hpp +++ b/higan/ws/ws.hpp @@ -20,6 +20,14 @@ namespace WonderSwan { #include namespace WonderSwan { + enum class Model : uint { + WonderSwan, //SW-001 (ASWAN) + WonderSwanColor, //WSC-001 (SPHINX) + SwanCrystal, //SCT-001 (SPHINX2) + }; + + enum : uint { Byte = 1, Word = 2, Long = 4 }; + struct Thread { ~Thread() { if(thread) co_delete(thread); @@ -42,8 +50,6 @@ namespace WonderSwan { int64 clock = 0; }; - enum : uint { Byte = 1, Word = 2, Long = 4 }; - #include #include #include @@ -52,10 +58,6 @@ namespace WonderSwan { #include #include #include - - inline auto WS() { return system.model() == System::Model::WonderSwan; } - inline auto WSC() { return system.model() == System::Model::WonderSwanColor; } - inline auto SC() { return system.model() == System::Model::SwanCrystal; } } #include