Update to v101r13 release.

byuu says:

Changelog:

  - MS: added ms/bus
  - Z80: implemented JP/JR/CP/DI/IM/IN instructions
  - MD/VDP: added window layer emulation
  - MD/controller/gamepad: fixed d2,d3 bits (Altered Beast requires
    this)

The Z80 is definitely a lot nastier than the LR35902. There's a lot of
table duplication with HL→IX→IY; and two of them nest two levels deep
(eg FD CB xx xx), so the design may change as I implement more.
This commit is contained in:
Tim Allen 2016-08-27 14:48:21 +10:00
parent 5df717ff2a
commit 7c96826eb0
21 changed files with 459 additions and 72 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "101.12"; static const string Version = "101.13";
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/";

View File

@ -30,6 +30,13 @@ auto APU::write(uint16 addr, uint8 data) -> void {
step(1); step(1);
} }
auto APU::in(uint8 addr) -> uint8 {
return 0x00;
}
auto APU::out(uint8 addr, uint8 data) -> void {
}
auto APU::power() -> void { auto APU::power() -> void {
} }

View File

@ -8,6 +8,8 @@ struct APU : Processor::Z80, Thread {
auto wait() -> void override; auto wait() -> void override;
auto read(uint16 addr) -> uint8 override; auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override; auto write(uint16 addr, uint8 data) -> void override;
auto in(uint8 addr) -> uint8 override;
auto out(uint8 addr, uint8 data) -> void override;
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;

View File

@ -7,6 +7,8 @@ auto Gamepad::readData() -> uint8 {
if(select == 0) { if(select == 0) {
data.bit(0) = interface->inputPoll(port, ID::Device::Gamepad, Up); data.bit(0) = interface->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(1) = interface->inputPoll(port, ID::Device::Gamepad, Down); data.bit(1) = interface->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(2) = 1;
data.bit(3) = 1;
data.bit(4) = interface->inputPoll(port, ID::Device::Gamepad, A); data.bit(4) = interface->inputPoll(port, ID::Device::Gamepad, A);
data.bit(5) = interface->inputPoll(port, ID::Device::Gamepad, Start); data.bit(5) = interface->inputPoll(port, ID::Device::Gamepad, Start);
} else { } else {

View File

@ -1,27 +1,68 @@
auto VDP::Background::scanline(uint y) -> void { auto VDP::Background::isWindowed(uint x, uint y) -> bool {
if((x < io.horizontalOffset) ^ io.horizontalDirection) return true;
if((y < io.verticalOffset ) ^ io.verticalDirection ) return true;
return false;
}
auto VDP::Background::updateHorizontalScroll(uint y) -> void {
if(id == ID::Window) return;
uint15 address = io.horizontalScrollAddress; uint15 address = io.horizontalScrollAddress;
static const uint mask[] = {0u, 7u, ~7u, ~0u}; static const uint mask[] = {0u, 7u, ~7u, ~0u};
address += (y & mask[io.horizontalScrollMode]) << 1; address += (y & mask[io.horizontalScrollMode]) << 1;
address += (this == &vdp.planeB); address += id == ID::PlaneB;
state.horizontalScroll = vdp.vram[address].bits(0,9); state.horizontalScroll = vdp.vram[address].bits(0,9);
} }
auto VDP::Background::updateVerticalScroll(uint x, uint y) -> void {
if(id == ID::Window) return;
auto address = (x >> 4 & 0 - io.verticalScrollMode) << 1;
address += id == ID::PlaneB;
state.verticalScroll = vdp.vsram[address];
}
auto VDP::Background::nametableAddress() -> uint15 {
if(id == ID::Window && vdp.screenWidth() == 320) return io.nametableAddress & ~0x0400;
return io.nametableAddress;
}
auto VDP::Background::nametableWidth() -> uint {
if(id == ID::Window) return vdp.screenWidth() == 320 ? 64 : 32;
return 32 * (1 + io.nametableWidth);
}
auto VDP::Background::nametableHeight() -> uint {
if(id == ID::Window) return 32;
return 32 * (1 + io.nametableHeight);
}
auto VDP::Background::scanline(uint y) -> void {
updateHorizontalScroll(y);
}
auto VDP::Background::run(uint x, uint y) -> void { auto VDP::Background::run(uint x, uint y) -> void {
updateVerticalScroll(x, y);
output.priority = 0; output.priority = 0;
output.color = 0; output.color = 0;
static const uint tiles[] = {32, 64, 96, 128};
y += vdp.vsram[((x >> 4) & (io.verticalScrollMode ? ~0u : 0u)) * 2 + (this == &vdp.planeB)];
x -= state.horizontalScroll; x -= state.horizontalScroll;
y += state.verticalScroll;
uint tileX = x >> 3 & (tiles[io.nametableWidth ] - 1); uint width = nametableWidth();
uint tileY = y >> 3 & (tiles[io.nametableHeight] - 1); uint height = nametableHeight();
uint15 nametableAddress = io.nametableAddress;
nametableAddress += (tileY * tiles[io.nametableWidth] + tileX) & 0x0fff;
uint16 tileAttributes = vdp.vram[nametableAddress]; uint tileX = x >> 3 & width - 1;
uint tileY = y >> 3 & height - 1;
auto address = nametableAddress();
address += (tileY * width + tileX) & 0x0fff;
uint16 tileAttributes = vdp.vram[address];
uint15 tileAddress = tileAttributes.bits(0,10) << 4; uint15 tileAddress = tileAttributes.bits(0,10) << 4;
uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0); uint pixelX = (x & 7) ^ (tileAttributes.bit(11) ? 7 : 0);
uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0); uint pixelY = (y & 7) ^ (tileAttributes.bit(12) ? 7 : 0);

View File

@ -217,10 +217,8 @@ auto VDP::writeControlPort(uint16 data) -> void {
//mode register 3 //mode register 3
case 0x0b: { case 0x0b: {
planeA.io.horizontalScrollMode = data.bits(0,1); planeA.io.horizontalScrollMode = data.bits(0,1);
window.io.horizontalScrollMode = data.bits(0,1);
planeB.io.horizontalScrollMode = data.bits(0,1); planeB.io.horizontalScrollMode = data.bits(0,1);
planeA.io.verticalScrollMode = data.bit(2); planeA.io.verticalScrollMode = data.bit(2);
window.io.verticalScrollMode = data.bit(2);
planeB.io.verticalScrollMode = data.bit(2); planeB.io.verticalScrollMode = data.bit(2);
io.externalInterruptEnable = data.bit(3); io.externalInterruptEnable = data.bit(3);
return; return;
@ -240,7 +238,6 @@ auto VDP::writeControlPort(uint16 data) -> void {
//horizontal scroll data location //horizontal scroll data location
case 0x0d: { case 0x0d: {
planeA.io.horizontalScrollAddress = data.bits(0,6) << 9; planeA.io.horizontalScrollAddress = data.bits(0,6) << 9;
window.io.horizontalScrollAddress = data.bits(0,6) << 9;
planeB.io.horizontalScrollAddress = data.bits(0,6) << 9; planeB.io.horizontalScrollAddress = data.bits(0,6) << 9;
return; return;
} }
@ -261,47 +258,23 @@ auto VDP::writeControlPort(uint16 data) -> void {
//plane size //plane size
case 0x10: { case 0x10: {
planeA.io.nametableWidth = data.bits(0,1); planeA.io.nametableWidth = data.bits(0,1);
window.io.nametableWidth = data.bits(0,1);
planeB.io.nametableWidth = data.bits(0,1); planeB.io.nametableWidth = data.bits(0,1);
planeA.io.nametableHeight = data.bits(4,5); planeA.io.nametableHeight = data.bits(4,5);
window.io.nametableHeight = data.bits(4,5);
planeB.io.nametableHeight = data.bits(4,5); planeB.io.nametableHeight = data.bits(4,5);
return; return;
} }
//window plane horizontal position //window plane horizontal position
case 0x11: { case 0x11: {
if(!data) { window.io.horizontalDirection = data.bit(7);
//disable window.io.horizontalOffset = data.bits(0,4) << 4;
io.windowHorizontalLo = ~0;
io.windowHorizontalHi = ~0;
} else if(data.bit(7) == 0) {
//left
io.windowHorizontalLo = 0;
io.windowHorizontalHi = (data.bits(0,4) << 4) - 1;
} else {
//right
io.windowHorizontalLo = (data.bits(0,4) << 4) - 1;
io.windowHorizontalHi = ~0;
}
return; return;
} }
//window plane vertical position //window plane vertical position
case 0x12: { case 0x12: {
if(!data) { window.io.verticalDirection = data.bit(7);
//disable window.io.verticalOffset = data.bits(0,4) << 3;
io.windowVerticalLo = ~0;
io.windowVerticalHi = ~0;
} else if(data.bit(7) == 0) {
//up
io.windowVerticalLo = 0;
io.windowVerticalHi = (data.bits(0,4) << 3) - 1;
} else {
//down
io.windowVerticalLo = (data.bits(0,4) << 3) - 1;
io.windowVerticalHi = ~0;
}
return; return;
} }

View File

@ -18,11 +18,7 @@ auto VDP::run() -> void {
if(!io.displayEnable) return outputPixel(0); if(!io.displayEnable) return outputPixel(0);
if(state.y >= screenHeight()) return outputPixel(0); if(state.y >= screenHeight()) return outputPixel(0);
bool windowed = false; //todo: broken auto& planeA = window.isWindowed(state.x, state.y) ? window : this->planeA;
windowed &= state.x >= io.windowHorizontalLo && state.x <= io.windowHorizontalHi;
windowed &= state.y >= io.windowVerticalLo && state.y <= io.windowVerticalHi;
auto& planeA = windowed ? this->window : this->planeA;
planeA.run(state.x, state.y); planeA.run(state.x, state.y);
planeB.run(state.x, state.y); planeB.run(state.x, state.y);
sprite.run(state.x, state.y); sprite.run(state.x, state.y);

View File

@ -32,6 +32,17 @@ struct VDP : Thread {
//background.cpp //background.cpp
struct Background { struct Background {
enum class ID : uint { PlaneA, Window, PlaneB } id;
auto isWindowed(uint x, uint y) -> bool;
auto updateHorizontalScroll(uint y) -> void;
auto updateVerticalScroll(uint x, uint y) -> void;
auto nametableAddress() -> uint15;
auto nametableWidth() -> uint;
auto nametableHeight() -> uint;
auto scanline(uint y) -> void; auto scanline(uint y) -> void;
auto run(uint x, uint y) -> void; auto run(uint x, uint y) -> void;
@ -40,15 +51,24 @@ struct VDP : Thread {
struct IO { struct IO {
uint15 nametableAddress; uint15 nametableAddress;
//PlaneA, PlaneB
uint2 nametableWidth; uint2 nametableWidth;
uint2 nametableHeight; uint2 nametableHeight;
uint15 horizontalScrollAddress; uint15 horizontalScrollAddress;
uint2 horizontalScrollMode; uint2 horizontalScrollMode;
uint1 verticalScrollMode; uint1 verticalScrollMode;
//Window
uint1 horizontalDirection;
uint10 horizontalOffset;
uint1 verticalDirection;
uint10 verticalOffset;
} io; } io;
struct State { struct State {
uint10 horizontalScroll; uint10 horizontalScroll;
uint10 verticalScroll;
} state; } state;
struct Output { struct Output {
@ -56,9 +76,9 @@ struct VDP : Thread {
boolean priority; boolean priority;
} output; } output;
}; };
Background planeA; Background planeA{Background::ID::PlaneA};
Background window; Background window{Background::ID::Window};
Background planeB; Background planeB{Background::ID::PlaneB};
//sprite.cpp //sprite.cpp
struct Sprite { struct Sprite {
@ -154,14 +174,6 @@ private:
//$0f data port auto-increment value //$0f data port auto-increment value
uint8 dataIncrement; uint8 dataIncrement;
//$11 window plane horizontal position
uint10 windowHorizontalLo;
uint10 windowHorizontalHi;
//$12 window plane vertical position
uint10 windowVerticalLo;
uint10 windowVerticalHi;
//$13-$14 DMA length //$13-$14 DMA length
uint16 dmaLength; uint16 dmaLength;

View File

@ -2,7 +2,7 @@ processors += z80
objects += ms-interface objects += ms-interface
objects += ms-cpu ms-vdp ms-psg objects += ms-cpu ms-vdp ms-psg
objects += ms-system ms-cartridge objects += ms-system ms-cartridge ms-bus
obj/ms-interface.o: ms/interface/interface.cpp $(call rwildcard,ms/interface) obj/ms-interface.o: ms/interface/interface.cpp $(call rwildcard,ms/interface)
obj/ms-cpu.o: ms/cpu/cpu.cpp $(call rwildcard,ms/cpu) obj/ms-cpu.o: ms/cpu/cpu.cpp $(call rwildcard,ms/cpu)
@ -10,3 +10,4 @@ obj/ms-vdp.o: ms/vdp/vdp.cpp $(call rwildcard,ms/vdp)
obj/ms-psg.o: ms/psg/psg.cpp $(call rwildcard,ms/psg) obj/ms-psg.o: ms/psg/psg.cpp $(call rwildcard,ms/psg)
obj/ms-system.o: ms/system/system.cpp $(call rwildcard,ms/system) obj/ms-system.o: ms/system/system.cpp $(call rwildcard,ms/system)
obj/ms-cartridge.o: ms/cartridge/cartridge.cpp $(call rwildcard,ms/cartridge) obj/ms-cartridge.o: ms/cartridge/cartridge.cpp $(call rwildcard,ms/cartridge)
obj/ms-bus.o: ms/bus/bus.cpp $(call rwildcard,ms/bus)

28
higan/ms/bus/bus.cpp Normal file
View File

@ -0,0 +1,28 @@
#include <ms/ms.hpp>
namespace MasterSystem {
Bus bus;
auto Bus::read(uint16 addr) -> uint8 {
if(addr < 0xc000) return cartridge.read(addr);
return ram[addr & 0x1fff];
}
auto Bus::write(uint16 addr, uint8 data) -> void {
if(addr < 0xc000) return cartridge.write(addr, data);
ram[addr & 0x1fff] = data;
}
auto Bus::in(uint8 addr) -> uint8 {
switch(addr) {
case 0x7e: return vdp.in(addr);
case 0x7f: return vdp.in(addr);
}
return 0x00;
}
auto Bus::out(uint8 addr, uint8 data) -> void {
}
}

12
higan/ms/bus/bus.hpp Normal file
View File

@ -0,0 +1,12 @@
struct Bus {
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto in(uint8 addr) -> uint8;
auto out(uint8 addr, uint8 data) -> void;
private:
uint8 ram[0x2000];
};
extern Bus bus;

View File

@ -24,14 +24,22 @@ auto CPU::wait() -> void {
auto CPU::read(uint16 addr) -> uint8 { auto CPU::read(uint16 addr) -> uint8 {
step(1); step(1);
if(addr < 0xc000) return cartridge.read(addr); return bus.read(addr);
return ram[addr & 0x1fff];
} }
auto CPU::write(uint16 addr, uint8 data) -> void { auto CPU::write(uint16 addr, uint8 data) -> void {
step(1); step(1);
if(addr < 0xc000) return cartridge.write(addr, data); return bus.write(addr, data);
ram[addr & 0x1fff] = data; }
auto CPU::in(uint8 addr) -> uint8 {
step(1);
return bus.in(addr);
}
auto CPU::out(uint8 addr, uint8 data) -> void {
step(1);
return bus.out(addr, data);
} }
auto CPU::power() -> void { auto CPU::power() -> void {

View File

@ -8,12 +8,11 @@ struct CPU : Processor::Z80, Thread {
auto wait() -> void override; auto wait() -> void override;
auto read(uint16 addr) -> uint8 override; auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override; auto write(uint16 addr, uint8 data) -> void override;
auto in(uint8 addr) -> uint8 override;
auto out(uint8 addr, uint8 data) -> void override;
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;
private:
uint8 ram[0x2000]; //8KB
}; };
extern CPU cpu; extern CPU cpu;

View File

@ -36,6 +36,7 @@ namespace MasterSystem {
#include <ms/system/system.hpp> #include <ms/system/system.hpp>
#include <ms/cartridge/cartridge.hpp> #include <ms/cartridge/cartridge.hpp>
#include <ms/bus/bus.hpp>
} }
#include <ms/interface/interface.hpp> #include <ms/interface/interface.hpp>

View File

@ -26,6 +26,18 @@ auto VDP::refresh() -> void {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240); Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
} }
auto VDP::in(uint8 addr) -> uint8 {
switch(addr) {
}
return 0xb0;
}
auto VDP::out(uint8 addr, uint8 data) -> void {
switch(addr) {
}
}
auto VDP::power() -> void { auto VDP::power() -> void {
} }

View File

@ -6,6 +6,9 @@ struct VDP : Thread {
auto step(uint clocks) -> void; auto step(uint clocks) -> void;
auto refresh() -> void; auto refresh() -> void;
auto in(uint8 addr) -> uint8;
auto out(uint8 addr, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;

View File

@ -0,0 +1,120 @@
auto Z80::disassemble(uint16 pc) -> string {
string output{hex(pc, 4L), " "};
output.append(pad(disassembleOpcode(pc), -18, ' '));
output.append(
" AF:", hex(r.af, 4L),
" BC:", hex(r.bc, 4L),
" DE:", hex(r.de, 4L),
" HL:", hex(r.hl, 4L),
" IX:", hex(r.ix, 4L),
" IY:", hex(r.iy, 4L),
" SP:", hex(r.sp, 4L)
);
return output;
}
#define X hex(x, 2L)
#define Y hex(y, 2L)
#define Z hex(z, 2L)
#define W hex(y << 8 | x << 0, 4L)
#define R hex(pc + 2 + (int8)x, 4L)
auto Z80::disassembleOpcode(uint16 pc) -> string {
auto o = read(pc + 0);
auto x = read(pc + 1);
auto y = read(pc + 2);
auto z = read(pc + 3);
switch(o) {
case 0x00: return {"nop "};
case 0x18: return {"jr $", R};
case 0x20: return {"jr nz,$", R};
case 0x28: return {"jr z,$", R};
case 0x30: return {"jr nc,$", R};
case 0x38: return {"jr c,$", R};
case 0xb8: return {"cp b"};
case 0xb9: return {"cp c"};
case 0xba: return {"cp d"};
case 0xbb: return {"cp e"};
case 0xbc: return {"cp h"};
case 0xbd: return {"cp l"};
case 0xbe: return {"cp (hl)"};
case 0xbf: return {"cp a"};
case 0xc2: return {"jp nz,$", W};
case 0xc3: return {"jp $", W};
case 0xca: return {"jp z,$", W};
case 0xd2: return {"jp nc,$", W};
case 0xda: return {"jp c,$", W};
case 0xdb: return {"in a,($", X, ")"};
case 0xdd: return disassembleOpcodeDD(pc + 1);
case 0xe2: return {"jp po,$", W};
case 0xe9: return {"jp hl"};
case 0xea: return {"jp pe,$", W};
case 0xed: return disassembleOpcodeED(pc + 1);
case 0xf2: return {"jp p,$", W};
case 0xf3: return {"di "};
case 0xfa: return {"jp m,$", W};
case 0xfd: return disassembleOpcodeFD(pc + 1);
case 0xfe: return {"cp $", X};
}
return {"??? (", hex(o, 2L), " ", X, " ", Y, " ", Z, ")"};
}
auto Z80::disassembleOpcodeDD(uint16 pc) -> string {
auto o = read(pc + 0);
auto x = read(pc + 1);
auto y = read(pc + 2);
auto z = read(pc + 3);
switch(o) {
case 0xe9: return {"jp ix"};
}
return {"??? (dd ", hex(o, 2L), " ", X, " ", Y, ")"};
}
auto Z80::disassembleOpcodeED(uint16 pc) -> string {
auto o = read(pc + 0);
auto x = read(pc + 1);
auto y = read(pc + 2);
auto z = read(pc + 3);
switch(o) {
case 0x40: return {"in b,(c)"};
case 0x46: return {"im 0"};
case 0x48: return {"in c,(c)"};
case 0x50: return {"in d,(c)"};
case 0x56: return {"im 1"};
case 0x58: return {"in e,(c)"};
case 0x5e: return {"im 2"};
case 0x60: return {"in h,(c)"};
case 0x68: return {"in l,(c)"};
case 0x70: return {"in (c)"};
case 0x78: return {"in a,(c)"};
}
return {"??? (ed ", hex(o, 2L), " ", X, " ", Y, ")"};
}
auto Z80::disassembleOpcodeFD(uint16 pc) -> string {
auto o = read(pc + 0);
auto x = read(pc + 1);
auto y = read(pc + 2);
auto z = read(pc + 3);
switch(o) {
case 0xe9: return {"jp iy"};
}
return {"??? (fd ", hex(o, 2L), " ", X, " ", Y, ")"};
}
#undef X
#undef Y
#undef Z
#undef W
#undef R

View File

@ -1,6 +1,17 @@
auto Z80::trap(uint8_t prefix, uint8_t opcode) -> void {
print("[Z80] unimplemented instruction: ", prefix ? pad(hex(prefix, 2L), ' ', -3) : "", hex(opcode, 2L), "\n");
print("[Z80] instructions executed: ", --instructionsExecuted, "\n");
while(true) wait();
}
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__); #define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
auto Z80::instruction() -> void { auto Z80::instruction() -> void {
#if 1
if(instructionsExecuted < 20)
print(disassemble(r.pc), "\n");
#endif
instructionsExecuted++; instructionsExecuted++;
r.r = (r.r & 0x80) | (r.r + 1 & 0x7f); r.r = (r.r & 0x80) | (r.r + 1 & 0x7f);
@ -8,12 +19,71 @@ auto Z80::instruction() -> void {
auto opcode = read(r.pc++); auto opcode = read(r.pc++);
switch(opcode) { switch(opcode) {
op(0x00, NOP) op(0x00, NOP)
op(0x18, JR_c, true)
op(0x20, JR_c, r.p.z == 0)
op(0x28, JR_c, r.p.z == 1)
op(0x30, JR_c, r.p.c == 0)
op(0x38, JR_c, r.p.c == 1)
op(0xb8, CP_r, r.b)
op(0xb9, CP_r, r.c)
op(0xba, CP_r, r.d)
op(0xbb, CP_r, r.e)
op(0xbc, CP_r, r.h)
op(0xbd, CP_r, r.l)
op(0xbe, CP_ihl)
op(0xbf, CP_r, r.a)
op(0xc2, JP_c_nn, r.p.z == 0)
op(0xc3, JP_c_nn, true)
op(0xca, JP_c_nn, r.p.z == 1)
op(0xd2, JP_c_nn, r.p.c == 0)
op(0xda, JP_c_nn, r.p.c == 1)
op(0xdb, IN_a_in)
op(0xdd, DD)
op(0xe2, JP_c_nn, r.p.p == 0)
op(0xe9, JP_rr, r.hl)
op(0xea, JP_c_nn, r.p.p == 1)
op(0xed, ED)
op(0xf2, JP_c_nn, r.p.s == 0)
op(0xf3, DI) op(0xf3, DI)
op(0xfa, JP_c_nn, r.p.s == 1)
op(0xfd, FD)
op(0xfe, CP_n)
} }
trap(0x00, opcode);
}
print("[Z80] unimplemented instruction: ", hex(opcode, 2L), "\n"); auto Z80::instructionDD() -> void {
print("[Z80] instructions executed: ", --instructionsExecuted, "\n"); auto opcode = read(r.pc++);
while(true) wait(); switch(opcode) {
op(0xe9, JP_rr, r.ix)
}
trap(0xdd, opcode);
}
auto Z80::instructionED() -> void {
auto opcode = read(r.pc++);
switch(opcode) {
op(0x40, IN_r_ic, r.b)
op(0x46, IM, 0)
op(0x48, IN_r_ic, r.c)
op(0x50, IN_r_ic, r.d)
op(0x56, IM, 1)
op(0x58, IN_r_ic, r.e)
op(0x5e, IM, 2)
op(0x60, IN_r_ic, r.h)
op(0x68, IN_r_ic, r.l)
op(0x70, IN_r_ic, r.flag)
op(0x78, IN_r_ic, r.a)
}
trap(0xed, opcode);
}
auto Z80::instructionFD() -> void {
auto opcode = read(r.pc++);
switch(opcode) {
op(0xe9, JP_rr, r.iy)
}
trap(0xfd, opcode);
} }
#undef op #undef op

View File

@ -1,4 +1,60 @@
auto Z80::CP(uint8 x) -> void {
uint16 y = r.a - x;
r.p.c = y > 0xff;
r.p.n = 1;
r.p.v = (r.a ^ x) & (r.a ^ y) & 0x80;
r.p.h = (r.a ^ y ^ x) & 0x10;
r.p.z = y == 0;
r.p.s = y & 0x80;
}
auto Z80::instructionCP_ihl() -> void {
CP(read(r.hl));
}
auto Z80::instructionCP_n() -> void {
CP(read(r.pc++));
}
auto Z80::instructionCP_r(uint8_t& x) -> void {
CP(x);
}
auto Z80::instructionDI() -> void { auto Z80::instructionDI() -> void {
r.di = 1;
}
auto Z80::instructionIM(uint mode) -> void {
r.im = mode;
}
auto Z80::instructionIN_a_in() -> void {
r.a = in(read(r.pc++));
}
auto Z80::instructionIN_r_ic(uint8_t& x) -> void {
x = in(read(r.c));
r.p.n = 0;
r.p.p = parity(x);
r.p.h = 0;
r.p.z = x == 0;
r.p.s = x & 0x80;
}
auto Z80::instructionJP_c_nn(bool c) -> void {
auto lo = read(r.pc++);
auto hi = read(r.pc++);
if(c) r.pc = hi << 8 | lo << 0;
}
auto Z80::instructionJP_rr(uint16_t& x) -> void {
r.pc = x;
}
auto Z80::instructionJR_c(bool c) -> void {
auto d = read(r.pc++);
if(c) r.pc += (int8)d;
} }
auto Z80::instructionNOP() -> void { auto Z80::instructionNOP() -> void {

View File

@ -5,6 +5,7 @@ namespace Processor {
#include "instruction.cpp" #include "instruction.cpp"
#include "instructions.cpp" #include "instructions.cpp"
#include "disassembler.cpp"
auto Z80::power() -> void { auto Z80::power() -> void {
} }
@ -20,6 +21,17 @@ auto Z80::reset() -> void {
r.pc = 0x0000; r.pc = 0x0000;
r.i = 0x00; r.i = 0x00;
r.r = 0x00; r.r = 0x00;
r.di = false;
r.ei = false;
r.im = 0;
}
auto Z80::parity(uint8_t value) const -> bool {
value ^= value >> 4;
value ^= value >> 2;
value ^= value >> 1;
return !(value & 1);
} }
} }

View File

@ -8,31 +8,57 @@ struct Z80 {
virtual auto wait() -> void = 0; virtual auto wait() -> void = 0;
virtual auto read(uint16 addr) -> uint8 = 0; virtual auto read(uint16 addr) -> uint8 = 0;
virtual auto write(uint16 addr, uint8 data) -> void = 0; virtual auto write(uint16 addr, uint8 data) -> void = 0;
virtual auto in(uint8 addr) -> uint8 = 0;
virtual auto out(uint8 addr, uint8 data) -> void = 0;
//z80.cpp //z80.cpp
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;
auto parity(uint8_t) const -> bool;
//instruction.cpp //instruction.cpp
auto trap(uint8_t prefix, uint8_t opcode) -> void;
auto instruction() -> void; auto instruction() -> void;
auto instructionDD() -> void;
auto instructionED() -> void;
auto instructionFD() -> void;
//instructions.cpp //instructions.cpp
auto CP(uint8 x) -> void;
auto instructionCP_ihl() -> void;
auto instructionCP_n() -> void;
auto instructionCP_r(uint8_t&) -> void;
auto instructionDI() -> void; auto instructionDI() -> void;
auto instructionIM(uint) -> void;
auto instructionIN_a_in() -> void;
auto instructionIN_r_ic(uint8_t&) -> void;
auto instructionJP_c_nn(bool) -> void;
auto instructionJP_rr(uint16_t&) -> void;
auto instructionJR_c(bool) -> void;
auto instructionNOP() -> void; auto instructionNOP() -> void;
//disassembler.cpp
auto disassemble(uint16 pc) -> string;
auto disassembleOpcode(uint16 pc) -> string;
auto disassembleOpcodeDD(uint16 pc) -> string;
auto disassembleOpcodeED(uint16 pc) -> string;
auto disassembleOpcodeFD(uint16 pc) -> string;
struct Registers { struct Registers {
union { union {
uint16_t af; uint16_t af;
struct { uint8_t order_msb2(a, f); }; struct { uint8_t order_msb2(a, f); };
union { union {
BooleanBitField<uint16_t, 0> c; //carry BooleanBitField<uint16_t, 0> c; //carry
BooleanBitField<uint16_t, 1> s; //subtract BooleanBitField<uint16_t, 1> n; //add / subtract
BooleanBitField<uint16_t, 2> p; //parity
BooleanBitField<uint16_t, 2> v; //overflow BooleanBitField<uint16_t, 2> v; //overflow
//BooleanBitField<uint16_t, 3> _; //unused (copy of bit 3 of result) BooleanBitField<uint16_t, 3> x; //unused (copy of bit 3 of result)
BooleanBitField<uint16_t, 4> h; //half-carry BooleanBitField<uint16_t, 4> h; //half-carry
//BooleanBitField<uint16_t, 5> _; //unused (copy of bit 5 of result) BooleanBitField<uint16_t, 5> y; //unused (copy of bit 5 of result)
BooleanBitField<uint16_t, 6> z; //zero BooleanBitField<uint16_t, 6> z; //zero
BooleanBitField<uint16_t, 7> n; //negative BooleanBitField<uint16_t, 7> s; //sign
} p; } p;
}; };
@ -58,6 +84,12 @@ struct Z80 {
uint8_t i; uint8_t i;
uint8_t r; uint8_t r;
boolean di; //disable interrupt
boolean ei; //enable interrupt
uint2 im; //interrupt mode (0-2)
uint8_t flag;
} r; } r;
private: private: