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