mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
5df717ff2a
commit
7c96826eb0
|
@ -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/";
|
||||||
|
|
|
@ -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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue