mirror of https://github.com/bsnes-emu/bsnes.git
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 >_>
This commit is contained in:
parent
7a748e093e
commit
a89a3da77a
|
@ -6,7 +6,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
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 Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -83,11 +83,13 @@ auto V30MZ::opReturnFarImm() {
|
||||||
r.sp += offset;
|
r.sp += offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//cf iret
|
||||||
auto V30MZ::opReturnInt() {
|
auto V30MZ::opReturnInt() {
|
||||||
wait(9);
|
wait(9);
|
||||||
r.ip = pop();
|
r.ip = pop();
|
||||||
r.cs = pop();
|
r.cs = pop();
|
||||||
r.f = pop();
|
r.f = pop();
|
||||||
|
state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::opInt3() {
|
auto V30MZ::opInt3() {
|
||||||
|
@ -136,6 +138,7 @@ auto V30MZ::opPushReg(uint16& reg) {
|
||||||
|
|
||||||
auto V30MZ::opPopReg(uint16& reg) {
|
auto V30MZ::opPopReg(uint16& reg) {
|
||||||
reg = pop();
|
reg = pop();
|
||||||
|
if(® == &r.ss) state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//9c pushf
|
//9c pushf
|
||||||
|
@ -148,6 +151,7 @@ auto V30MZ::opPushFlags() {
|
||||||
auto V30MZ::opPopFlags() {
|
auto V30MZ::opPopFlags() {
|
||||||
wait(2);
|
wait(2);
|
||||||
r.f = pop();
|
r.f = pop();
|
||||||
|
state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//60 pusha
|
//60 pusha
|
||||||
|
|
|
@ -24,4 +24,5 @@ auto V30MZ::opClearFlag(bool& flag) {
|
||||||
auto V30MZ::opSetFlag(bool& flag) {
|
auto V30MZ::opSetFlag(bool& flag) {
|
||||||
wait(3);
|
wait(3);
|
||||||
flag = true;
|
flag = true;
|
||||||
|
if(&flag == &r.f.i) state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,22 +3,25 @@
|
||||||
//36 ss:
|
//36 ss:
|
||||||
//3e ds:
|
//3e ds:
|
||||||
auto V30MZ::opSegment(uint16 segment) {
|
auto V30MZ::opSegment(uint16 segment) {
|
||||||
state.prefix = true;
|
|
||||||
prefix.segment = segment;
|
prefix.segment = segment;
|
||||||
|
state.prefix = true;
|
||||||
|
state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//f2 repnz
|
//f2 repnz:
|
||||||
//f3 repz
|
//f3 repz:
|
||||||
auto V30MZ::opRepeat(bool flag) {
|
auto V30MZ::opRepeat(bool flag) {
|
||||||
wait(4);
|
wait(4);
|
||||||
if(r.cx == 0) return;
|
if(r.cx == 0) return;
|
||||||
state.prefix = true;
|
|
||||||
prefix.repeat = flag;
|
prefix.repeat = flag;
|
||||||
|
state.prefix = true;
|
||||||
|
state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//f0 lock
|
//f0 lock:
|
||||||
auto V30MZ::opLock() {
|
auto V30MZ::opLock() {
|
||||||
state.prefix = true;
|
state.prefix = true;
|
||||||
|
state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//9b wait
|
//9b wait
|
||||||
|
|
|
@ -8,23 +8,27 @@ auto V30MZ::opMoveRegMem(Size size) {
|
||||||
setReg(size, getMem(size));
|
setReg(size, getMem(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//8c mov memw,seg
|
||||||
auto V30MZ::opMoveMemSeg() {
|
auto V30MZ::opMoveMemSeg() {
|
||||||
modRM();
|
modRM();
|
||||||
setMem(Word, getSeg());
|
setMem(Word, getSeg());
|
||||||
|
state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//8e mov seg,memw
|
||||||
auto V30MZ::opMoveSegMem() {
|
auto V30MZ::opMoveSegMem() {
|
||||||
wait(1);
|
wait(1);
|
||||||
modRM();
|
modRM();
|
||||||
setSeg(getMem(Word));
|
setSeg(getMem(Word));
|
||||||
|
if((modrm.reg & 3) == 3) state.poll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::opMoveAccMem(Size size) {
|
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) {
|
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) {
|
auto V30MZ::opMoveRegImm(uint8& reg) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ auto V30MZ::modRM() -> void {
|
||||||
case 3: modrm.segment = segment(r.ss); modrm.address = r.bp + r.di; break;
|
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 4: modrm.segment = segment(r.ds); modrm.address = r.si; break;
|
||||||
case 5: modrm.segment = segment(r.ds); modrm.address = r.di; 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;
|
case 7: modrm.segment = segment(r.ds); modrm.address = r.bx; break;
|
||||||
}
|
}
|
||||||
if(modrm.mod == 1) modrm.address += (int8)fetch(Byte);
|
if(modrm.mod == 1) modrm.address += (int8)fetch(Byte);
|
||||||
|
|
|
@ -18,9 +18,11 @@ namespace Processor {
|
||||||
#include "disassembler.cpp"
|
#include "disassembler.cpp"
|
||||||
|
|
||||||
auto V30MZ::exec() -> void {
|
auto V30MZ::exec() -> void {
|
||||||
|
state.poll = true;
|
||||||
if(state.halt) return wait(1);
|
if(state.halt) return wait(1);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
static uint counter = 0;
|
||||||
static uint16 cs = 0, ip = 0;
|
static uint16 cs = 0, ip = 0;
|
||||||
if(cs != r.cs || ip != r.ip) print(disassemble(cs = r.cs, ip = r.ip), "\n");
|
if(cs != r.cs || ip != r.ip) print(disassemble(cs = r.cs, ip = r.ip), "\n");
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,8 +32,8 @@ auto V30MZ::exec() -> void {
|
||||||
if(state.prefix) {
|
if(state.prefix) {
|
||||||
state.prefix = false;
|
state.prefix = false;
|
||||||
} else {
|
} else {
|
||||||
prefix.segment = nothing;
|
|
||||||
prefix.repeat = nothing;
|
prefix.repeat = nothing;
|
||||||
|
prefix.segment = nothing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,8 +304,8 @@ auto V30MZ::instruction() -> void {
|
||||||
auto V30MZ::interrupt(uint8 vector) -> void {
|
auto V30MZ::interrupt(uint8 vector) -> void {
|
||||||
state.halt = false;
|
state.halt = false;
|
||||||
|
|
||||||
auto ip = read(Word, 0x0000, vector * 2 + 0);
|
auto ip = read(Word, 0x0000, vector * 4 + 0);
|
||||||
auto cs = read(Word, 0x0000, vector * 2 + 2);
|
auto cs = read(Word, 0x0000, vector * 4 + 2);
|
||||||
|
|
||||||
push(r.f);
|
push(r.f);
|
||||||
push(r.cs);
|
push(r.cs);
|
||||||
|
@ -318,25 +320,26 @@ auto V30MZ::interrupt(uint8 vector) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto V30MZ::power() -> void {
|
auto V30MZ::power() -> void {
|
||||||
state.halt = false;
|
state.halt = false;
|
||||||
|
state.poll = true;
|
||||||
state.prefix = false;
|
state.prefix = false;
|
||||||
|
|
||||||
|
prefix.repeat = nothing;
|
||||||
prefix.segment = nothing;
|
prefix.segment = nothing;
|
||||||
prefix.repeat = nothing;
|
|
||||||
|
|
||||||
r.ip = 0x0000;
|
|
||||||
r.ax = 0x0000;
|
r.ax = 0x0000;
|
||||||
r.bx = 0x0000;
|
|
||||||
r.cx = 0x0000;
|
r.cx = 0x0000;
|
||||||
r.dx = 0x0000;
|
r.dx = 0x0000;
|
||||||
|
r.bx = 0x0000;
|
||||||
|
r.sp = 0x0000;
|
||||||
|
r.bp = 0x0000;
|
||||||
r.si = 0x0000;
|
r.si = 0x0000;
|
||||||
r.di = 0x0000;
|
r.di = 0x0000;
|
||||||
r.bp = 0x0000;
|
|
||||||
r.sp = 0x0000;
|
|
||||||
r.cs = 0xffff;
|
|
||||||
r.ds = 0x0000;
|
|
||||||
r.es = 0x0000;
|
r.es = 0x0000;
|
||||||
|
r.cs = 0xffff;
|
||||||
r.ss = 0x0000;
|
r.ss = 0x0000;
|
||||||
|
r.ds = 0x0000;
|
||||||
|
r.ip = 0x0000;
|
||||||
r.f = 0x8000;
|
r.f = 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ namespace Processor {
|
||||||
struct V30MZ {
|
struct V30MZ {
|
||||||
using Size = uint;
|
using Size = uint;
|
||||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||||
struct ModRM;
|
|
||||||
|
|
||||||
virtual auto wait(uint clocks = 1) -> void = 0;
|
virtual auto wait(uint clocks = 1) -> void = 0;
|
||||||
virtual auto read(uint20 addr) -> uint8 = 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;
|
auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
bool halt;
|
bool halt; //set to true for hlt instruction; blocks execution until next interrupt
|
||||||
bool prefix;
|
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;
|
} state;
|
||||||
|
|
||||||
struct Prefix {
|
struct Prefix {
|
||||||
maybe<uint16> segment;
|
maybe<bool> repeat; //repnz, repz
|
||||||
maybe<bool> repeat;
|
maybe<uint16> segment; //cs, es, ss, ds
|
||||||
} prefix;
|
} prefix;
|
||||||
|
|
||||||
struct ModRM {
|
struct ModRM {
|
||||||
|
@ -211,24 +211,24 @@ struct V30MZ {
|
||||||
uint3 reg;
|
uint3 reg;
|
||||||
uint3 mem;
|
uint3 mem;
|
||||||
|
|
||||||
uint16 address;
|
|
||||||
uint16 segment;
|
uint16 segment;
|
||||||
|
uint16 address;
|
||||||
} modrm;
|
} modrm;
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
uint16 ip;
|
|
||||||
union { uint16 ax; struct { uint8 order_lsb2(al, ah); }; };
|
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 cx; struct { uint8 order_lsb2(cl, ch); }; };
|
||||||
union { uint16 dx; struct { uint8 order_lsb2(dl, dh); }; };
|
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 si;
|
||||||
uint16 di;
|
uint16 di;
|
||||||
uint16 bp;
|
|
||||||
uint16 sp;
|
|
||||||
uint16 cs;
|
|
||||||
uint16 ds;
|
|
||||||
uint16 es;
|
uint16 es;
|
||||||
|
uint16 cs;
|
||||||
uint16 ss;
|
uint16 ss;
|
||||||
|
uint16 ds;
|
||||||
|
uint16 ip;
|
||||||
|
|
||||||
uint8* b[8]{&al, &cl, &dl, &bl, &ah, &ch, &dh, &bh};
|
uint8* b[8]{&al, &cl, &dl, &bl, &ah, &ch, &dh, &bh};
|
||||||
uint16* w[8]{&ax, &cx, &dx, &bx, &sp, &bp, &si, &di};
|
uint16* w[8]{&ax, &cx, &dx, &bx, &sp, &bp, &si, &di};
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace WonderSwan {
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
#include "io.cpp"
|
#include "io.cpp"
|
||||||
|
#include "interrupt.cpp"
|
||||||
#include "dma.cpp"
|
#include "dma.cpp"
|
||||||
|
|
||||||
auto CPU::Enter() -> void {
|
auto CPU::Enter() -> void {
|
||||||
|
@ -18,6 +19,7 @@ auto CPU::main() -> void {
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
poll();
|
||||||
exec();
|
exec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,17 +57,24 @@ auto CPU::power() -> void {
|
||||||
create(CPU::Enter, 3072000);
|
create(CPU::Enter, 3072000);
|
||||||
|
|
||||||
iomap[0x00a0] = this;
|
iomap[0x00a0] = this;
|
||||||
|
iomap[0x00b0] = this;
|
||||||
|
iomap[0x00b2] = this;
|
||||||
|
iomap[0x00b4] = this;
|
||||||
|
iomap[0x00b6] = this;
|
||||||
|
|
||||||
if(WSC() || SC()) {
|
if(WSC() || SC()) {
|
||||||
for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this;
|
for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this;
|
||||||
iomap[0x0062] = this;
|
iomap[0x0062] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.dmaSource = 0x00000;
|
r.dmaSource = 0x00000;
|
||||||
s.dmaTarget = 0x0000;
|
r.dmaTarget = 0x0000;
|
||||||
s.dmaLength = 0x0000;
|
r.dmaLength = 0x0000;
|
||||||
s.dmaEnable = false;
|
r.dmaEnable = false;
|
||||||
s.dmaMode = 0;
|
r.dmaMode = 0;
|
||||||
|
r.interruptBase = 0x00;
|
||||||
|
r.interruptEnable = 0x00;
|
||||||
|
r.interruptStatus = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
struct CPU : Processor::V30MZ, Thread, IO {
|
struct CPU : Processor::V30MZ, Thread, IO {
|
||||||
|
enum class Interrupt : uint {
|
||||||
|
SerialSend,
|
||||||
|
Input,
|
||||||
|
Cartridge,
|
||||||
|
SerialReceive,
|
||||||
|
LineCompare,
|
||||||
|
VblankTimer,
|
||||||
|
Vblank,
|
||||||
|
HblankTimer,
|
||||||
|
};
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
|
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
@ -20,23 +31,37 @@ struct CPU : Processor::V30MZ, Thread, IO {
|
||||||
auto portRead(uint16 addr) -> uint8 override;
|
auto portRead(uint16 addr) -> uint8 override;
|
||||||
auto portWrite(uint16 addr, uint8 data) -> void override;
|
auto portWrite(uint16 addr, uint8 data) -> void override;
|
||||||
|
|
||||||
|
//interrupt.cpp
|
||||||
|
auto poll() -> void;
|
||||||
|
auto raise(Interrupt) -> void;
|
||||||
|
auto lower(Interrupt) -> void;
|
||||||
|
|
||||||
//dma.cpp
|
//dma.cpp
|
||||||
auto dmaTransfer() -> void;
|
auto dmaTransfer() -> void;
|
||||||
|
|
||||||
struct State {
|
struct Registers {
|
||||||
//$0040-0042 DMA_SRC
|
//$0040-0042 DMA_SRC
|
||||||
uint20 dmaSource;
|
uint20 dmaSource;
|
||||||
|
|
||||||
//$0044-0045 DMA_DST
|
//$0044-0045 DMA_DST
|
||||||
uint16_ dmaTarget;
|
uint16_ dmaTarget;
|
||||||
|
|
||||||
//$0046-0047 DMA_LEN
|
//$0046-0047 DMA_LEN
|
||||||
uint16_ dmaLength;
|
uint16_ dmaLength;
|
||||||
|
|
||||||
//$0048 DMA_CTRL
|
//$0048 DMA_CTRL
|
||||||
bool dmaEnable;
|
bool dmaEnable;
|
||||||
bool dmaMode; //0 = increment; 1 = decrement
|
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;
|
extern CPU cpu;
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
auto CPU::dmaTransfer() -> void {
|
auto CPU::dmaTransfer() -> void {
|
||||||
//length of 0 or SRAM source address cause immediate termination
|
//length of 0 or SRAM source address cause immediate termination
|
||||||
if(s.dmaLength == 0 || s.dmaSource.b2 == 1) {
|
if(r.dmaLength == 0 || r.dmaSource.b2 == 1) {
|
||||||
s.dmaEnable = false;
|
r.dmaEnable = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wait(5);
|
wait(5);
|
||||||
while(s.dmaLength) {
|
while(r.dmaLength) {
|
||||||
wait(2);
|
wait(2);
|
||||||
uint16 data = 0;
|
uint16 data = 0;
|
||||||
//once DMA is started; SRAM reads still incur time penalty, but do not transfer
|
//once DMA is started; SRAM reads still incur time penalty, but do not transfer
|
||||||
if(s.dmaSource.b2 != 1) {
|
if(r.dmaSource.b2 != 1) {
|
||||||
data |= read(s.dmaSource + 0) << 0;
|
data |= read(r.dmaSource + 0) << 0;
|
||||||
data |= read(s.dmaSource + 1) << 8;
|
data |= read(r.dmaSource + 1) << 8;
|
||||||
write(s.dmaTarget + 0, data >> 0);
|
write(r.dmaTarget + 0, data >> 0);
|
||||||
write(s.dmaTarget + 1, data >> 8);
|
write(r.dmaTarget + 1, data >> 8);
|
||||||
}
|
}
|
||||||
if(s.dmaMode == 0) {
|
if(r.dmaMode == 0) {
|
||||||
s.dmaSource += 2;
|
r.dmaSource += 2;
|
||||||
s.dmaTarget += 2;
|
r.dmaTarget += 2;
|
||||||
} else {
|
} else {
|
||||||
s.dmaSource -= 2;
|
r.dmaSource -= 2;
|
||||||
s.dmaTarget -= 2;
|
r.dmaTarget -= 2;
|
||||||
}
|
}
|
||||||
s.dmaLength -= 2;
|
r.dmaLength -= 2;
|
||||||
};
|
};
|
||||||
s.dmaEnable = false;
|
r.dmaEnable = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
auto CPU::portRead(uint16 addr) -> uint8 {
|
auto CPU::portRead(uint16 addr) -> uint8 {
|
||||||
//DMA_SRC
|
//DMA_SRC
|
||||||
if(addr == 0x0040) return s.dmaSource.b0;
|
if(addr == 0x0040) return r.dmaSource.b0;
|
||||||
if(addr == 0x0041) return s.dmaSource.b1;
|
if(addr == 0x0041) return r.dmaSource.b1;
|
||||||
if(addr == 0x0042) return s.dmaSource.b2;
|
if(addr == 0x0042) return r.dmaSource.b2;
|
||||||
|
|
||||||
//DMA_DST
|
//DMA_DST
|
||||||
if(addr == 0x0044) return s.dmaTarget.b0;
|
if(addr == 0x0044) return r.dmaTarget.b0;
|
||||||
if(addr == 0x0045) return s.dmaTarget.b1;
|
if(addr == 0x0045) return r.dmaTarget.b1;
|
||||||
|
|
||||||
//DMA_LEN
|
//DMA_LEN
|
||||||
if(addr == 0x0046) return s.dmaLength.b0;
|
if(addr == 0x0046) return r.dmaLength.b0;
|
||||||
if(addr == 0x0047) return s.dmaLength.b1;
|
if(addr == 0x0047) return r.dmaLength.b1;
|
||||||
|
|
||||||
//DMA_CTRL
|
//DMA_CTRL
|
||||||
if(addr == 0x0048) return s.dmaEnable << 7 | s.dmaMode << 0;
|
if(addr == 0x0048) return r.dmaEnable << 7 | r.dmaMode << 0;
|
||||||
|
|
||||||
//WSC_SYSTEM
|
//WSC_SYSTEM
|
||||||
if(addr == 0x0062) {
|
if(addr == 0x0062) {
|
||||||
|
@ -22,44 +22,78 @@ auto CPU::portRead(uint16 addr) -> uint8 {
|
||||||
|
|
||||||
//HW_FLAGS
|
//HW_FLAGS
|
||||||
if(addr == 0x00a0) {
|
if(addr == 0x00a0) {
|
||||||
return 1 << 7 //1 = built-in self-test passed
|
return (
|
||||||
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
|
1 << 7 //1 = built-in self-test passed
|
||||||
| !WS() << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal
|
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
|
||||||
| 1 << 0; //0 = BIOS mapped; 1 = cartridge mapped
|
| !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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::portWrite(uint16 addr, uint8 data) -> void {
|
auto CPU::portWrite(uint16 addr, uint8 data) -> void {
|
||||||
//DMA_SRC
|
//DMA_SRC
|
||||||
if(addr == 0x0040) { s.dmaSource.b0 = data & ~1; return; }
|
if(addr == 0x0040) { r.dmaSource.b0 = data & ~1; return; }
|
||||||
if(addr == 0x0041) { s.dmaSource.b1 = data; return; }
|
if(addr == 0x0041) { r.dmaSource.b1 = data; return; }
|
||||||
if(addr == 0x0042) { s.dmaSource.b2 = data; return; }
|
if(addr == 0x0042) { r.dmaSource.b2 = data; return; }
|
||||||
|
|
||||||
//DMA_DST
|
//DMA_DST
|
||||||
if(addr == 0x0044) { s.dmaTarget.b0 = data & ~1; return; }
|
if(addr == 0x0044) { r.dmaTarget.b0 = data & ~1; return; }
|
||||||
if(addr == 0x0045) { s.dmaTarget.b1 = data; return; }
|
if(addr == 0x0045) { r.dmaTarget.b1 = data; return; }
|
||||||
|
|
||||||
//DMA_LEN
|
//DMA_LEN
|
||||||
if(addr == 0x0046) { s.dmaLength.b0 = data & ~1; return; }
|
if(addr == 0x0046) { r.dmaLength.b0 = data & ~1; return; }
|
||||||
if(addr == 0x0047) { s.dmaLength.b1 = data; return; }
|
if(addr == 0x0047) { r.dmaLength.b1 = data; return; }
|
||||||
|
|
||||||
//DMA_CTRL
|
//DMA_CTRL
|
||||||
if(addr == 0x0048) {
|
if(addr == 0x0048) {
|
||||||
s.dmaEnable = (uint1)(data >> 7);
|
r.dmaEnable = (uint1)(data >> 7);
|
||||||
s.dmaMode = (uint1)(data >> 0);
|
r.dmaMode = (uint1)(data >> 0);
|
||||||
if(s.dmaEnable) dmaTransfer();
|
if(r.dmaEnable) dmaTransfer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//WSC_SYSTEM
|
//WSC_SYSTEM
|
||||||
if(addr == 0x0062) {
|
if(addr == 0x0062) {
|
||||||
//todo: d0 = 1 powers off system
|
//todo: d0 = 1 powers off system
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//HW_FLAGS
|
//HW_FLAGS
|
||||||
if(addr == 0x00a0) {
|
if(addr == 0x00a0) {
|
||||||
//todo: d2 (bus width) bit is writable; but ... it will do very bad things
|
//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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,24 @@ auto PPU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
step(256);
|
step(256);
|
||||||
|
scanline();
|
||||||
status.hclk = 0;
|
|
||||||
if(++status.vclk == 159) {
|
|
||||||
status.vclk = 0;
|
|
||||||
video.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
auto PPU::step(uint clocks) -> void {
|
||||||
status.hclk += clocks;
|
status.hclk += clocks;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ struct PPU : Thread, IO {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
|
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
auto scanline() -> void;
|
||||||
|
auto frame() -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto step(uint clocks) -> void;
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue