Update to v101r25 release.

byuu says:

Changelog:

  - Makefile: added $(windres), -lpthread to Windows port
  - GBA: WAITCNT.prefetch is not writable (should fix Donkey Kong: King
    of Swing) \[endrift\]
  - SMS: fixed hcounter shift value \[hex\_usr\]
  - SMS: emulated interrupts (reset button isn't hooked up anywhere, not
    sure where to put it yet)

This WIP actually took a really long time because the documentation on
SMS interrupts was all over the place. I'm hoping I've emulated them
correctly, but I honestly have no idea. It's based off my best
understanding from four or five different sources. So it's probably
quite buggy.

However, a few interrupts fire in Sonic the Hedgehog, so that's
something to start with. Now I just have to hope I've gotten some games
far enough in that I can start seeing some data in the VDP VRAM. I need
that before I can start emulating graphics mode 4 to get some actual
screen output.

Or I can just say to hell with it and use a "Hello World" test ROM.
That'd probably be smarter.
This commit is contained in:
Tim Allen 2016-12-26 23:09:56 +11:00
parent bab2ac812a
commit e30780bb72
14 changed files with 144 additions and 24 deletions

View File

@ -24,7 +24,7 @@ ifeq ($(platform),windows)
else
link += -mwindows
endif
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -mthreads -lpthread -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
link += -Wl,-enable-auto-import
link += -Wl,-enable-runtime-pseudo-reloc
else ifeq ($(platform),macosx)

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "101.24";
static const string Version = "101.25";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -148,7 +148,7 @@ auto CPU::power() -> void {
for(auto& swait : regs.wait.control.swait) swait = 0;
regs.wait.control.phi = 0;
regs.wait.control.prefetch = 0;
regs.wait.control.gametype = 0;
regs.wait.control.gametype = 0; //0 = GBA, 1 = GBC
regs.postboot = 0;
regs.mode = Registers::Mode::Normal;
regs.clock = 0;

View File

@ -383,7 +383,7 @@ auto CPU::writeIO(uint32 addr, uint8 data) -> void {
regs.wait.control.swait[2] = data.bit (2);
regs.wait.control.phi = data.bit (3);
regs.wait.control.prefetch = data.bit (6);
regs.wait.control.gametype = data.bit (7);
//regs.wait.control.gametype is read-only
return;
//IME

View File

@ -10,11 +10,24 @@ auto CPU::Enter() -> void {
auto CPU::main() -> void {
#if 1
static uint64 instructionsExecuted = 0;
if(instructionsExecuted < 20)
print(disassemble(r.pc), "\n");
instructionsExecuted++;
#endif
//note: SMS1 extbus value is random; SMS2+ is pulled high ($ff)
if(state.nmiLine) {
state.nmiLine = 0; //edge-sensitive
irq(0, 0x0066, 0xff);
}
if(state.intLine) {
//level-sensitive
irq(1, 0x0038, 0xff);
}
instruction();
}
@ -24,6 +37,14 @@ auto CPU::step(uint clocks) -> void {
synchronize(psg);
}
auto CPU::setNMI(bool value) -> void {
state.nmiLine = value;
}
auto CPU::setINT(bool value) -> void {
state.intLine = value;
}
auto CPU::power() -> void {
Z80::bus = &MasterSystem::bus;
Z80::power();
@ -33,7 +54,7 @@ auto CPU::reset() -> void {
Z80::reset();
create(CPU::Enter, system.colorburst());
instructionsExecuted = 0;
memory::fill(&state, sizeof(State));
}
}

View File

@ -5,11 +5,17 @@ struct CPU : Processor::Z80, Thread {
auto main() -> void;
auto step(uint clocks) -> void;
auto setNMI(bool value) -> void;
auto setINT(bool value) -> void;
auto power() -> void;
auto reset() -> void;
private:
uint64 instructionsExecuted;
struct State {
boolean nmiLine;
boolean intLine;
} state;
};
extern CPU cpu;

View File

@ -14,7 +14,7 @@ auto VDP::vcounter() -> uint8 {
}
auto VDP::hcounter() -> uint8 {
uint hcounter = io.hcounter >> 2;
uint hcounter = io.hcounter >> 1;
return hcounter <= 233 ? hcounter : hcounter - 86;
}
@ -29,7 +29,19 @@ auto VDP::data() -> uint8 {
auto VDP::status() -> uint8 {
io.controlLatch = 0;
return 0x00;
uint8 result = 0x00;
result |= io.intFrame << 7;
result |= io.spriteOverflow << 6;
result |= io.spriteCollision << 5;
result |= io.fifthSprite << 0;
io.intLine = 0;
io.intFrame = 0;
io.spriteOverflow = 0;
io.spriteCollision = 0;
io.fifthSprite = 0;
return result;
}
auto VDP::data(uint8 data) -> void {

View File

@ -10,15 +10,27 @@ auto VDP::Enter() -> void {
}
auto VDP::main() -> void {
for(uint y : range(262)) {
if(io.vcounter <= vlines()) {
if(io.lcounter-- == 0) {
io.lcounter = io.lineCounter;
io.intLine = 1;
}
}
if(io.vcounter == vlines() + 1) {
io.lcounter = io.lineCounter;
io.intFrame = 1;
}
for(uint x : range(684)) {
step(1);
}
if(y == 240) scheduler.exit(Scheduler::Event::Frame);
}
if(io.vcounter == 240) scheduler.exit(Scheduler::Event::Frame);
}
auto VDP::step(uint clocks) -> void {
while(clocks--) {
if(++io.hcounter == 684) {
io.hcounter = 0;
if(++io.vcounter == 262) {
@ -26,14 +38,26 @@ auto VDP::step(uint clocks) -> void {
}
}
Thread::step(clocks);
cpu.setINT((io.lineInterrupts && io.intLine) || (io.frameInterrupts && io.intFrame));
Thread::step(1);
synchronize(cpu);
}
}
auto VDP::refresh() -> void {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
}
auto VDP::vlines() -> uint {
if(io.lines240) return 240;
if(io.lines224) return 224;
return 192;
}
auto VDP::vblank() -> bool {
return io.vcounter >= vlines();
}
auto VDP::power() -> void {
}

View File

@ -6,6 +6,9 @@ struct VDP : Thread {
auto step(uint clocks) -> void;
auto refresh() -> void;
auto vlines() -> uint;
auto vblank() -> bool;
auto power() -> void;
auto reset() -> void;
@ -25,9 +28,20 @@ private:
uint8 cram[0x40];
struct IO {
uint vcounter;
uint hcounter;
uint vcounter; //vertical counter
uint hcounter; //horizontal counter
uint lcounter; //line counter
//interrupt flags
bool intLine;
bool intFrame;
//status flags
bool spriteOverflow;
bool spriteCollision;
uint5 fifthSprite;
//latches
bool controlLatch;
uint16 controlData;
uint2 code;

View File

@ -2,6 +2,13 @@ auto Z80::instruction() -> void {
auto code = opcode();
if(code == 0xdd) { r.hlp = &r.ix; return; }
if(code == 0xfd) { r.hlp = &r.iy; return; }
if(r.ei) {
r.ei = 0;
r.iff1 = 1;
r.iff2 = 1;
}
instruction__(code);
r.hlp = &r.hl;
}

View File

@ -433,8 +433,7 @@ auto Z80::instructionDJNZ_e() -> void {
}
auto Z80::instructionEI() -> void {
r.iff1 = 1;
r.iff2 = 1;
r.ei = 1; //raise IFF1, IFF2 after the next instruction
}
auto Z80::instructionEX_rr_rr(uint16& x, uint16& y) -> void {

View File

@ -17,6 +17,40 @@ auto Z80::reset() -> void {
r.hlp = &r.hl;
}
auto Z80::irq(bool maskable, uint16 pc, uint8 extbus) -> bool {
if(maskable && !r.iff1) return false;
push(r.pc);
switch(r.im) {
case 0: {
//external data bus ($ff = RST $38)
r.pc = extbus;
break;
}
case 1: {
//constant address
r.pc = pc;
break;
}
case 2: {
//vector table with external data bus
uint16 addr = (r.ir.byte.hi << 8) | extbus;
r.pc = read(addr + 0) << 0;
r.pc |= read(addr + 1) << 8;
break;
}
}
r.iff1 = 0;
if(maskable) r.iff2 = 0;
return true;
}
auto Z80::parity(uint8 value) const -> bool {
value ^= value >> 4;
value ^= value >> 2;

View File

@ -18,6 +18,7 @@ struct Z80 {
auto power() -> void;
auto reset() -> void;
auto irq(bool maskable, uint16 vector = 0x0000, uint8 extbus = 0xff) -> bool;
auto parity(uint8) const -> bool;
//memory.cpp
@ -203,6 +204,7 @@ struct Z80 {
uint16 sp;
uint16 pc;
boolean ei; //EI instruction executed
boolean halt; //HALT instruction executed
boolean iff1; //interrupt flip-flop 1
boolean iff2; //interrupt flip-flop 2

View File

@ -20,6 +20,7 @@ ifeq ($(platform),windows)
ruby += video.direct3d video.wgl video.directdraw video.gdi
ruby += audio.wasapi audio.xaudio2 audio.directsound
ruby += input.windows
windres := windres
else ifeq ($(platform),macosx)
ruby += video.cgl
ruby += audio.openal
@ -62,7 +63,7 @@ obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/)
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
obj/ui-resource.o:
windres data/resource.rc obj/ui-resource.o
$(windres) data/resource.rc obj/ui-resource.o
# targets
build: $(objects)