From a89a3da77a5be314f05d32830e3194326471cac4 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 5 Feb 2016 08:18:06 +1100 Subject: [PATCH] Update to v097r11 release. byuu says: Alright, well interrupts are in. At least Vblank is. I also fixed a bug in vector() indexing, MoDRM mod!=3&®==6 using SS instead of DS, opcodes a0-a3 allowing segment override, and added the "irq_disable" stuff to the relevant opcodes to suppress IRQs after certain instructions. But unfortunately ... still no go on Riviera. It's not reading any unmapped ports, and although it enables Vblank IRQs and they set port $b4's status bit, the game never sets the IE flag, so no interrupts ever actually fire. The game does indeed appear to be sitting in a rather huge loop, which is probably dependent upon some RAM variable being set from the Vblank IRQ, but I don't know how I'm supposed to be triggering it. ... I'm really quite stumped here >_> --- higan/emulator/emulator.hpp | 2 +- higan/processor/v30mz/instructions-exec.cpp | 4 ++ higan/processor/v30mz/instructions-flag.cpp | 1 + higan/processor/v30mz/instructions-misc.cpp | 13 ++-- higan/processor/v30mz/instructions-move.cpp | 8 ++- higan/processor/v30mz/modrm.cpp | 2 +- higan/processor/v30mz/v30mz.cpp | 25 ++++--- higan/processor/v30mz/v30mz.hpp | 24 +++---- higan/ws/cpu/cpu.cpp | 19 +++-- higan/ws/cpu/cpu.hpp | 37 ++++++++-- higan/ws/cpu/dma.cpp | 30 ++++---- higan/ws/cpu/interrupt.cpp | 21 ++++++ higan/ws/cpu/io.cpp | 78 +++++++++++++++------ higan/ws/ppu/ppu.cpp | 21 ++++-- higan/ws/ppu/ppu.hpp | 2 + 15 files changed, 201 insertions(+), 86 deletions(-) create mode 100644 higan/ws/cpu/interrupt.cpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index dc3d4229..a6d11ee1 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.10"; + static const string Version = "097.11"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/processor/v30mz/instructions-exec.cpp b/higan/processor/v30mz/instructions-exec.cpp index 74f61b95..54d59cfa 100644 --- a/higan/processor/v30mz/instructions-exec.cpp +++ b/higan/processor/v30mz/instructions-exec.cpp @@ -83,11 +83,13 @@ auto V30MZ::opReturnFarImm() { r.sp += offset; } +//cf iret auto V30MZ::opReturnInt() { wait(9); r.ip = pop(); r.cs = pop(); r.f = pop(); + state.poll = false; } auto V30MZ::opInt3() { @@ -136,6 +138,7 @@ auto V30MZ::opPushReg(uint16& reg) { auto V30MZ::opPopReg(uint16& reg) { reg = pop(); + if(® == &r.ss) state.poll = false; } //9c pushf @@ -148,6 +151,7 @@ auto V30MZ::opPushFlags() { auto V30MZ::opPopFlags() { wait(2); r.f = pop(); + state.poll = false; } //60 pusha diff --git a/higan/processor/v30mz/instructions-flag.cpp b/higan/processor/v30mz/instructions-flag.cpp index d814105e..f1f66c0a 100644 --- a/higan/processor/v30mz/instructions-flag.cpp +++ b/higan/processor/v30mz/instructions-flag.cpp @@ -24,4 +24,5 @@ auto V30MZ::opClearFlag(bool& flag) { auto V30MZ::opSetFlag(bool& flag) { wait(3); flag = true; + if(&flag == &r.f.i) state.poll = false; } diff --git a/higan/processor/v30mz/instructions-misc.cpp b/higan/processor/v30mz/instructions-misc.cpp index ad48c58d..0fa4c105 100644 --- a/higan/processor/v30mz/instructions-misc.cpp +++ b/higan/processor/v30mz/instructions-misc.cpp @@ -3,22 +3,25 @@ //36 ss: //3e ds: auto V30MZ::opSegment(uint16 segment) { - state.prefix = true; prefix.segment = segment; + state.prefix = true; + state.poll = false; } -//f2 repnz -//f3 repz +//f2 repnz: +//f3 repz: auto V30MZ::opRepeat(bool flag) { wait(4); if(r.cx == 0) return; - state.prefix = true; prefix.repeat = flag; + state.prefix = true; + state.poll = false; } -//f0 lock +//f0 lock: auto V30MZ::opLock() { state.prefix = true; + state.poll = false; } //9b wait diff --git a/higan/processor/v30mz/instructions-move.cpp b/higan/processor/v30mz/instructions-move.cpp index 509338f7..4c44597e 100644 --- a/higan/processor/v30mz/instructions-move.cpp +++ b/higan/processor/v30mz/instructions-move.cpp @@ -8,23 +8,27 @@ auto V30MZ::opMoveRegMem(Size size) { setReg(size, getMem(size)); } +//8c mov memw,seg auto V30MZ::opMoveMemSeg() { modRM(); setMem(Word, getSeg()); + state.poll = false; } +//8e mov seg,memw auto V30MZ::opMoveSegMem() { wait(1); modRM(); setSeg(getMem(Word)); + if((modrm.reg & 3) == 3) state.poll = false; } auto V30MZ::opMoveAccMem(Size size) { - setAcc(size, read(size, r.ds, fetch(Word))); + setAcc(size, read(size, segment(r.ds), fetch(Word))); } auto V30MZ::opMoveMemAcc(Size size) { - write(size, r.ds, fetch(Word), getAcc(size)); + write(size, segment(r.ds), fetch(Word), getAcc(size)); } auto V30MZ::opMoveRegImm(uint8& reg) { diff --git a/higan/processor/v30mz/modrm.cpp b/higan/processor/v30mz/modrm.cpp index 32df2ce0..06b3bb3b 100644 --- a/higan/processor/v30mz/modrm.cpp +++ b/higan/processor/v30mz/modrm.cpp @@ -20,7 +20,7 @@ auto V30MZ::modRM() -> void { case 3: modrm.segment = segment(r.ss); modrm.address = r.bp + r.di; break; case 4: modrm.segment = segment(r.ds); modrm.address = r.si; break; case 5: modrm.segment = segment(r.ds); modrm.address = r.di; break; - case 6: modrm.segment = segment(r.ds); modrm.address = r.bp; break; + case 6: modrm.segment = segment(r.ss); modrm.address = r.bp; break; case 7: modrm.segment = segment(r.ds); modrm.address = r.bx; break; } if(modrm.mod == 1) modrm.address += (int8)fetch(Byte); diff --git a/higan/processor/v30mz/v30mz.cpp b/higan/processor/v30mz/v30mz.cpp index a6fc46d6..386b4f3a 100644 --- a/higan/processor/v30mz/v30mz.cpp +++ b/higan/processor/v30mz/v30mz.cpp @@ -18,9 +18,11 @@ namespace Processor { #include "disassembler.cpp" auto V30MZ::exec() -> void { + state.poll = true; 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 @@ -30,8 +32,8 @@ auto V30MZ::exec() -> void { if(state.prefix) { state.prefix = false; } else { - prefix.segment = nothing; prefix.repeat = nothing; + prefix.segment = nothing; } } @@ -302,8 +304,8 @@ auto V30MZ::instruction() -> void { auto V30MZ::interrupt(uint8 vector) -> void { state.halt = false; - auto ip = read(Word, 0x0000, vector * 2 + 0); - auto cs = read(Word, 0x0000, vector * 2 + 2); + auto ip = read(Word, 0x0000, vector * 4 + 0); + auto cs = read(Word, 0x0000, vector * 4 + 2); push(r.f); push(r.cs); @@ -318,25 +320,26 @@ auto V30MZ::interrupt(uint8 vector) -> void { } auto V30MZ::power() -> void { - state.halt = false; + state.halt = false; + state.poll = true; state.prefix = false; + prefix.repeat = nothing; prefix.segment = nothing; - prefix.repeat = nothing; - r.ip = 0x0000; r.ax = 0x0000; - r.bx = 0x0000; r.cx = 0x0000; r.dx = 0x0000; + r.bx = 0x0000; + r.sp = 0x0000; + r.bp = 0x0000; r.si = 0x0000; r.di = 0x0000; - r.bp = 0x0000; - r.sp = 0x0000; - r.cs = 0xffff; - r.ds = 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 ee9d2ed1..22adfbe2 100644 --- a/higan/processor/v30mz/v30mz.hpp +++ b/higan/processor/v30mz/v30mz.hpp @@ -7,7 +7,6 @@ namespace Processor { struct V30MZ { using Size = uint; enum : uint { Byte = 1, Word = 2, Long = 4 }; - struct ModRM; virtual auto wait(uint clocks = 1) -> void = 0; virtual auto read(uint20 addr) -> uint8 = 0; @@ -197,13 +196,14 @@ struct V30MZ { auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string; struct State { - bool halt; - bool prefix; + bool halt; //set to true for hlt instruction; blocks execution until next interrupt + bool poll; //set to false to suppress interrupt polling between CPU instructions + bool prefix; //set to true for prefix instructions; prevents flushing of Prefix struct } state; struct Prefix { - maybe segment; - maybe repeat; + maybe repeat; //repnz, repz + maybe segment; //cs, es, ss, ds } prefix; struct ModRM { @@ -211,24 +211,24 @@ struct V30MZ { uint3 reg; uint3 mem; - uint16 address; uint16 segment; + uint16 address; } modrm; struct Registers { - uint16 ip; union { uint16 ax; struct { uint8 order_lsb2(al, ah); }; }; - union { uint16 bx; struct { uint8 order_lsb2(bl, bh); }; }; union { uint16 cx; struct { uint8 order_lsb2(cl, ch); }; }; union { uint16 dx; struct { uint8 order_lsb2(dl, dh); }; }; + union { uint16 bx; struct { uint8 order_lsb2(bl, bh); }; }; + uint16 sp; + uint16 bp; uint16 si; uint16 di; - uint16 bp; - uint16 sp; - uint16 cs; - uint16 ds; uint16 es; + uint16 cs; uint16 ss; + uint16 ds; + uint16 ip; uint8* b[8]{&al, &cl, &dl, &bl, &ah, &ch, &dh, &bh}; uint16* w[8]{&ax, &cx, &dx, &bx, &sp, &bp, &si, &di}; diff --git a/higan/ws/cpu/cpu.cpp b/higan/ws/cpu/cpu.cpp index e90cf093..3080f5de 100644 --- a/higan/ws/cpu/cpu.cpp +++ b/higan/ws/cpu/cpu.cpp @@ -5,6 +5,7 @@ namespace WonderSwan { CPU cpu; #include "memory.cpp" #include "io.cpp" +#include "interrupt.cpp" #include "dma.cpp" auto CPU::Enter() -> void { @@ -18,6 +19,7 @@ auto CPU::main() -> void { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } + poll(); exec(); } } @@ -55,17 +57,24 @@ auto CPU::power() -> void { create(CPU::Enter, 3072000); iomap[0x00a0] = this; + iomap[0x00b0] = this; + iomap[0x00b2] = this; + iomap[0x00b4] = this; + iomap[0x00b6] = this; if(WSC() || SC()) { for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this; iomap[0x0062] = this; } - s.dmaSource = 0x00000; - s.dmaTarget = 0x0000; - s.dmaLength = 0x0000; - s.dmaEnable = false; - s.dmaMode = 0; + r.dmaSource = 0x00000; + r.dmaTarget = 0x0000; + r.dmaLength = 0x0000; + r.dmaEnable = false; + r.dmaMode = 0; + r.interruptBase = 0x00; + r.interruptEnable = 0x00; + r.interruptStatus = 0x00; } } diff --git a/higan/ws/cpu/cpu.hpp b/higan/ws/cpu/cpu.hpp index 91bc7c85..8ad7427d 100644 --- a/higan/ws/cpu/cpu.hpp +++ b/higan/ws/cpu/cpu.hpp @@ -1,4 +1,15 @@ struct CPU : Processor::V30MZ, Thread, IO { + enum class Interrupt : uint { + SerialSend, + Input, + Cartridge, + SerialReceive, + LineCompare, + VblankTimer, + Vblank, + HblankTimer, + }; + static auto Enter() -> void; auto main() -> void; @@ -20,23 +31,37 @@ struct CPU : Processor::V30MZ, Thread, IO { auto portRead(uint16 addr) -> uint8 override; auto portWrite(uint16 addr, uint8 data) -> void override; + //interrupt.cpp + auto poll() -> void; + auto raise(Interrupt) -> void; + auto lower(Interrupt) -> void; + //dma.cpp auto dmaTransfer() -> void; - struct State { - //$0040-0042 DMA_SRC + struct Registers { + //$0040-0042 DMA_SRC uint20 dmaSource; - //$0044-0045 DMA_DST + //$0044-0045 DMA_DST uint16_ dmaTarget; - //$0046-0047 DMA_LEN + //$0046-0047 DMA_LEN uint16_ dmaLength; - //$0048 DMA_CTRL + //$0048 DMA_CTRL bool dmaEnable; bool dmaMode; //0 = increment; 1 = decrement - } s; + + //$00b0 INT_BASE + uint8 interruptBase; + + //$00b2 INT_ENABLE + uint8 interruptEnable; + + //$00b4 INT_STATUS + uint8 interruptStatus; + } r; }; extern CPU cpu; diff --git a/higan/ws/cpu/dma.cpp b/higan/ws/cpu/dma.cpp index f8b25794..6bf97b28 100644 --- a/higan/ws/cpu/dma.cpp +++ b/higan/ws/cpu/dma.cpp @@ -1,29 +1,29 @@ auto CPU::dmaTransfer() -> void { //length of 0 or SRAM source address cause immediate termination - if(s.dmaLength == 0 || s.dmaSource.b2 == 1) { - s.dmaEnable = false; + if(r.dmaLength == 0 || r.dmaSource.b2 == 1) { + r.dmaEnable = false; return; } wait(5); - while(s.dmaLength) { + while(r.dmaLength) { wait(2); uint16 data = 0; //once DMA is started; SRAM reads still incur time penalty, but do not transfer - if(s.dmaSource.b2 != 1) { - data |= read(s.dmaSource + 0) << 0; - data |= read(s.dmaSource + 1) << 8; - write(s.dmaTarget + 0, data >> 0); - write(s.dmaTarget + 1, data >> 8); + if(r.dmaSource.b2 != 1) { + data |= read(r.dmaSource + 0) << 0; + data |= read(r.dmaSource + 1) << 8; + write(r.dmaTarget + 0, data >> 0); + write(r.dmaTarget + 1, data >> 8); } - if(s.dmaMode == 0) { - s.dmaSource += 2; - s.dmaTarget += 2; + if(r.dmaMode == 0) { + r.dmaSource += 2; + r.dmaTarget += 2; } else { - s.dmaSource -= 2; - s.dmaTarget -= 2; + r.dmaSource -= 2; + r.dmaTarget -= 2; } - s.dmaLength -= 2; + r.dmaLength -= 2; }; - s.dmaEnable = false; + r.dmaEnable = false; } diff --git a/higan/ws/cpu/interrupt.cpp b/higan/ws/cpu/interrupt.cpp new file mode 100644 index 00000000..a5db055e --- /dev/null +++ b/higan/ws/cpu/interrupt.cpp @@ -0,0 +1,21 @@ +auto CPU::poll() -> void { + if(!state.poll || !(r.interruptStatus & r.interruptEnable)) return; + state.halt = false; + if(!V30MZ::r.f.i) return; + + for(int n = 7; n >= 0; n--) { + if(r.interruptStatus & r.interruptEnable & (1 << n)) { + interrupt(r.interruptBase + n); + } + } +} + +auto CPU::raise(Interrupt i) -> void { + auto mask = 1 << (uint)i; + r.interruptStatus |= mask; +} + +auto CPU::lower(Interrupt i) -> void { + auto mask = 1 << (uint)i; + r.interruptStatus &= ~mask; +} diff --git a/higan/ws/cpu/io.cpp b/higan/ws/cpu/io.cpp index 81c7512e..7a3bea99 100644 --- a/higan/ws/cpu/io.cpp +++ b/higan/ws/cpu/io.cpp @@ -1,19 +1,19 @@ auto CPU::portRead(uint16 addr) -> uint8 { //DMA_SRC - if(addr == 0x0040) return s.dmaSource.b0; - if(addr == 0x0041) return s.dmaSource.b1; - if(addr == 0x0042) return s.dmaSource.b2; + if(addr == 0x0040) return r.dmaSource.b0; + if(addr == 0x0041) return r.dmaSource.b1; + if(addr == 0x0042) return r.dmaSource.b2; //DMA_DST - if(addr == 0x0044) return s.dmaTarget.b0; - if(addr == 0x0045) return s.dmaTarget.b1; + if(addr == 0x0044) return r.dmaTarget.b0; + if(addr == 0x0045) return r.dmaTarget.b1; //DMA_LEN - if(addr == 0x0046) return s.dmaLength.b0; - if(addr == 0x0047) return s.dmaLength.b1; + if(addr == 0x0046) return r.dmaLength.b0; + if(addr == 0x0047) return r.dmaLength.b1; //DMA_CTRL - if(addr == 0x0048) return s.dmaEnable << 7 | s.dmaMode << 0; + if(addr == 0x0048) return r.dmaEnable << 7 | r.dmaMode << 0; //WSC_SYSTEM if(addr == 0x0062) { @@ -22,44 +22,78 @@ auto CPU::portRead(uint16 addr) -> uint8 { //HW_FLAGS if(addr == 0x00a0) { - 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 - | 1 << 0; //0 = BIOS mapped; 1 = cartridge mapped + 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 + | 1 << 0 //0 = BIOS mapped; 1 = cartridge mapped + ); } + //INT_BASE + if(addr == 0x00b0) { + if(WS()) return r.interruptBase | 3; + return r.interruptBase; + } + + //INT_ENABLE + if(addr == 0x00b2) return r.interruptEnable; + + //INT_STATUS + if(addr == 0x00b4) return r.interruptStatus; + return 0x00; } auto CPU::portWrite(uint16 addr, uint8 data) -> void { //DMA_SRC - if(addr == 0x0040) { s.dmaSource.b0 = data & ~1; return; } - if(addr == 0x0041) { s.dmaSource.b1 = data; return; } - if(addr == 0x0042) { s.dmaSource.b2 = data; return; } + if(addr == 0x0040) { r.dmaSource.b0 = data & ~1; return; } + if(addr == 0x0041) { r.dmaSource.b1 = data; return; } + if(addr == 0x0042) { r.dmaSource.b2 = data; return; } //DMA_DST - if(addr == 0x0044) { s.dmaTarget.b0 = data & ~1; return; } - if(addr == 0x0045) { s.dmaTarget.b1 = data; return; } + if(addr == 0x0044) { r.dmaTarget.b0 = data & ~1; return; } + if(addr == 0x0045) { r.dmaTarget.b1 = data; return; } //DMA_LEN - if(addr == 0x0046) { s.dmaLength.b0 = data & ~1; return; } - if(addr == 0x0047) { s.dmaLength.b1 = data; return; } + if(addr == 0x0046) { r.dmaLength.b0 = data & ~1; return; } + if(addr == 0x0047) { r.dmaLength.b1 = data; return; } //DMA_CTRL if(addr == 0x0048) { - s.dmaEnable = (uint1)(data >> 7); - s.dmaMode = (uint1)(data >> 0); - if(s.dmaEnable) dmaTransfer(); + r.dmaEnable = (uint1)(data >> 7); + r.dmaMode = (uint1)(data >> 0); + if(r.dmaEnable) dmaTransfer(); return; } //WSC_SYSTEM if(addr == 0x0062) { //todo: d0 = 1 powers off system + return; } //HW_FLAGS if(addr == 0x00a0) { //todo: d2 (bus width) bit is writable; but ... it will do very bad things + return; + } + + //INT_BASE + if(addr == 0x00b0) { + r.interruptBase = WS() ? (data & ~7) : (data & ~1); + return; + } + + //INT_ENABLE + if(addr == 0x00b2) { + r.interruptEnable = data; + return; + } + + //INT_ACK + if(addr == 0x00b6) { + r.interruptStatus &= ~data; + return; } } diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index f8873512..b903c9fc 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -17,15 +17,24 @@ auto PPU::main() -> void { } step(256); - - status.hclk = 0; - if(++status.vclk == 159) { - status.vclk = 0; - video.refresh(); - } + scanline(); } } +auto PPU::scanline() -> void { + status.hclk = 0; + status.vclk++; + if(status.vclk == 144) { + cpu.raise(CPU::Interrupt::Vblank); + } + if(status.vclk == 159) frame(); +} + +auto PPU::frame() -> void { + status.vclk = 0; + video.refresh(); +} + auto PPU::step(uint clocks) -> void { status.hclk += clocks; diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index 8dd8c306..c2674ef0 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -4,6 +4,8 @@ struct PPU : Thread, IO { static auto Enter() -> void; auto main() -> void; + auto scanline() -> void; + auto frame() -> void; auto step(uint clocks) -> void; auto power() -> void;