Update to v101r10 release.

byuu says:
Changelog:

  - 68K: MOVEQ is 8-bit signed
  - 68K: disassembler was print EOR for OR instructions
  - 68K: address/program-counter indexed mode had the signed-word/long
    bit backward
  - 68K: ADDQ/SUBQ #n,aN always works in long mode; regardless of size
  - 68K→VDP DMA needs to use `mode.bit(0)<<22|dmaSource`; increment by
    one instead of two
  - Z80: added registers and initial two instructions
  - MS: hooked up enough to load and start running games
      - Sonic the Hedgehog can execute exactly one instruction... whoo.
This commit is contained in:
Tim Allen 2016-08-20 00:11:26 +10:00
parent 4d2e17f9c0
commit 0b70a01b47
24 changed files with 390 additions and 27 deletions

View File

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

View File

@ -17,6 +17,19 @@ auto APU::step(uint clocks) -> void {
synchronize(cpu);
}
auto APU::wait() -> void {
step(1);
}
auto APU::read(uint16 addr) -> uint8 {
step(1);
return 0x00;
}
auto APU::write(uint16 addr, uint8 data) -> void {
step(1);
}
auto APU::power() -> void {
}

View File

@ -5,6 +5,10 @@ struct APU : Processor::Z80, Thread {
auto main() -> void;
auto step(uint clocks) -> void;
auto wait() -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
auto power() -> void;
auto reset() -> void;
};

View File

@ -17,7 +17,7 @@ auto CPU::boot() -> void {
auto CPU::main() -> void {
#if 0
static file fp;
if(!fp) fp.open({Path::user(), "Desktop/trace.log"}, file::mode::write);
if(!fp) fp.open({Path::user(), "Desktop/tracer.log"}, file::mode::write);
fp.print(pad(disassemble(r.pc), -60, ' '), " ", disassembleRegisters().replace("\n", " "), "\n");
#endif
@ -85,7 +85,7 @@ auto CPU::reset() -> void {
auto CPU::readByte(uint24 addr) -> uint8 {
if(addr < 0x400000) return cartridge.readByte(addr);
if(addr < 0xa00000) return 0x00;
if(addr < 0xc00000) return rand();
if(addr < 0xc00000) return rand(), 0;
if(addr < 0xe00000) return vdp.readByte(addr);
return ram[addr & 0xffff];
}
@ -93,7 +93,7 @@ auto CPU::readByte(uint24 addr) -> uint8 {
auto CPU::readWord(uint24 addr) -> uint16 {
if(addr < 0x400000) return cartridge.readWord(addr);
if(addr < 0xa00000) return 0x0000;
if(addr < 0xc00000) return rand();
if(addr < 0xc00000) return rand(), 0;
if(addr < 0xe00000) return vdp.readWord(addr);
uint16 data = ram[addr + 0 & 65535] << 8;
return data | ram[addr + 1 & 65535] << 0;

View File

@ -10,10 +10,10 @@ auto VDP::dmaRun() -> void {
auto VDP::dmaLoad() -> void {
cpu.wait |= Wait::VDP_DMA;
auto data = cpu.readWord(io.dmaSource);
auto data = cpu.readWord(io.dmaMode.bit(0) << 23 | io.dmaSource << 1);
writeDataPort(data);
io.dmaSource.bits(0,15) += 2;
io.dmaSource.bits(0,15)++;
if(--io.dmaLength == 0) {
io.command.bit(5) = 0;
cpu.wait &=~ Wait::VDP_DMA;

View File

@ -4,4 +4,86 @@ namespace MasterSystem {
Cartridge cartridge;
auto Cartridge::load() -> bool {
information = {};
switch(system.model()) {
case Model::MasterSystem:
if(auto pathID = interface->load(ID::MasterSystem, "Master System", "ms")) {
information.pathID = pathID();
} else return false;
break;
case Model::GameGear:
if(auto pathID = interface->load(ID::GameGear, "Game Gear", "gg")) {
information.pathID = pathID();
} else return false;
break;
}
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads();
} else return false;
auto document = BML::unserialize(information.manifest);
information.title = document["information/title"].text();
if(auto node = document["board/rom"]) {
rom.size = node["size"].natural();
rom.mask = bit::round(rom.size) - 1;
if(rom.size) {
rom.data = new uint8[rom.mask + 1];
if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, rom.size);
}
}
}
}
if(auto node = document["board/ram"]) {
ram.size = node["size"].natural();
ram.mask = bit::round(ram.size) - 1;
if(ram.size) {
ram.data = new uint8[ram.mask + 1];
if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read)) {
fp->read(ram.data, ram.size);
}
}
}
}
return true;
}
auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size);
}
}
}
auto Cartridge::unload() -> void {
delete[] rom.data;
delete[] ram.data;
rom = {};
ram = {};
}
auto Cartridge::read(uint16 addr) -> uint8 {
return rom.data[addr & rom.mask];
}
auto Cartridge::write(uint16 addr, uint8 data) -> void {
}
auto Cartridge::power() -> void {
}
auto Cartridge::reset() -> void {
}
}

View File

@ -1,4 +1,35 @@
struct Cartridge {
auto pathID() const -> uint { return information.pathID; }
auto sha256() const -> string { return information.sha256; }
auto manifest() const -> string { return information.manifest; }
auto title() const -> string { return information.title; }
auto load() -> bool;
auto save() -> void;
auto unload() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto power() -> void;
auto reset() -> void;
private:
struct Information {
uint pathID = 0;
string sha256;
string manifest;
string title;
} information;
struct Memory {
uint8* data = nullptr;
uint size = 0;
uint mask = 0;
};
Memory rom;
Memory ram;
};
extern Cartridge cartridge;

View File

@ -9,10 +9,36 @@ auto CPU::Enter() -> void {
}
auto CPU::main() -> void {
instruction();
}
auto CPU::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(vdp);
synchronize(psg);
}
auto CPU::wait() -> void {
step(1);
}
auto CPU::read(uint16 addr) -> uint8 {
step(1);
if(addr < 0xc000) return cartridge.read(addr);
return ram[addr & 0x1fff];
}
auto CPU::write(uint16 addr, uint8 data) -> void {
step(1);
if(addr < 0xc000) return cartridge.write(addr, data);
ram[addr & 0x1fff] = data;
}
auto CPU::power() -> void {
}
auto CPU::reset() -> void {
create(CPU::Enter, system.colorburst());
}
}

View File

@ -4,6 +4,16 @@ struct CPU : Processor::Z80, Thread {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto wait() -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
auto power() -> void;
auto reset() -> void;
private:
uint8 ram[0x2000]; //8KB
};
extern CPU cpu;

View File

@ -32,25 +32,29 @@ Interface::Interface() {
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
}
auto Interface::manifest() -> string {
return "";
return cartridge.manifest();
}
auto Interface::title() -> string {
return "";
return cartridge.title();
}
auto Interface::videoSize() -> VideoSize {
return {256, 192};
return {256, 240};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 256;
uint h = 192;
uint m = min(width / w, height / h);
return {w * m, h * m};
uint h = 240;
uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)};
}
auto Interface::videoFrequency() -> double {
@ -70,26 +74,33 @@ auto Interface::audioFrequency() -> double {
}
auto Interface::loaded() -> bool {
return false;
return system.loaded();
}
auto Interface::load(uint id) -> bool {
if(id == ID::MasterSystem) return system.load(Model::MasterSystem);
if(id == ID::GameGear) return system.load(Model::GameGear);
return false;
}
auto Interface::save() -> void {
system.save();
}
auto Interface::unload() -> void {
system.unload();
}
auto Interface::power() -> void {
system.power();
}
auto Interface::reset() -> void {
system.reset();
}
auto Interface::run() -> void {
system.run();
}
auto Interface::serialize() -> serializer {

View File

@ -14,6 +14,11 @@ namespace MasterSystem {
using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler;
enum class Model : uint {
MasterSystem,
GameGear,
};
struct Thread : Emulator::Thread {
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
Emulator::Thread::create(entrypoint, frequency);

View File

@ -9,10 +9,19 @@ auto PSG::Enter() -> void {
}
auto PSG::main() -> void {
step(1);
}
auto PSG::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
}
auto PSG::power() -> void {
}
auto PSG::reset() -> void {
create(PSG::Enter, system.colorburst());
}
}

View File

@ -4,6 +4,9 @@ struct PSG : Thread {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;
auto reset() -> void;
};
extern PSG psg;

View File

@ -6,22 +6,54 @@ System system;
Scheduler scheduler;
auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) vdp.refresh();
}
auto System::load() -> bool {
return false;
auto System::load(Model model) -> bool {
information = {};
information.model = model;
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads();
} else return false;
auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false;
information.colorburst = Emulator::Constants::Colorburst::NTSC;
return information.loaded = true;
}
auto System::save() -> void {
cartridge.save();
}
auto System::unload() -> void {
cartridge.unload();
}
auto System::power() -> void {
cartridge.power();
cpu.power();
vdp.power();
psg.power();
reset();
}
auto System::reset() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
Emulator::video.setPalette();
Emulator::audio.reset();
Emulator::audio.setInterface(interface);
scheduler.reset();
cartridge.reset();
cpu.reset();
vdp.reset();
psg.reset();
scheduler.primary(cpu);
}
}

View File

@ -1,15 +1,24 @@
struct System {
auto loaded() const -> bool { return false; }
auto colorburst() const -> double { return 0.0; }
auto loaded() const -> bool { return information.loaded; }
auto model() const -> Model { return information.model; }
auto colorburst() const -> double { return information.colorburst; }
auto run() -> void;
auto load() -> bool;
auto load(Model model) -> bool;
auto save() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
private:
struct Information {
bool loaded = false;
Model model = Model::MasterSystem;
string manifest;
double colorburst = 0.0;
} information;
};
extern System system;

View File

@ -9,10 +9,28 @@ auto VDP::Enter() -> void {
}
auto VDP::main() -> void {
for(uint y : range(262)) {
for(uint x : range(342)) {
step(1);
}
if(y == 240) scheduler.exit(Scheduler::Event::Frame);
}
}
auto VDP::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
}
auto VDP::refresh() -> void {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
}
auto VDP::power() -> void {
}
auto VDP::reset() -> void {
create(VDP::Enter, system.colorburst());
}
}

View File

@ -1,9 +1,16 @@
//...
//TI TMS9918A (derivative)
struct VDP : Thread {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto refresh() -> void;
auto power() -> void;
auto reset() -> void;
private:
uint32 buffer[256 * 240];
};
extern VDP vdp;

View File

@ -432,11 +432,11 @@ template<uint Size> auto M68K::disassembleNOT(EffectiveAddress with) -> string {
}
template<uint Size> auto M68K::disassembleOR(EffectiveAddress from, DataRegister with) -> string {
return {"eor", _suffix<Size>(), " ", _effectiveAddress<Size>(from), ",", _dataRegister(with)};
return {"or", _suffix<Size>(), " ", _effectiveAddress<Size>(from), ",", _dataRegister(with)};
}
template<uint Size> auto M68K::disassembleOR(DataRegister from, EffectiveAddress with) -> string {
return {"eor", _suffix<Size>(), " ", _dataRegister(from), ",", _effectiveAddress<Size>(with)};
return {"or", _suffix<Size>(), " ", _dataRegister(from), ",", _effectiveAddress<Size>(with)};
}
template<uint Size> auto M68K::disassembleORI(EffectiveAddress with) -> string {

View File

@ -32,7 +32,7 @@ template<uint Size> auto M68K::fetch(EffectiveAddress& ea) -> uint32 {
auto index = extension & 0x8000
? read(AddressRegister{extension >> 12})
: read(DataRegister{extension >> 12});
if(extension & 0x800) index = (int16)index;
if(!(extension & 0x800)) index = (int16)index;
return read(AddressRegister{ea.reg}) + index + (int8)extension;
}
@ -55,7 +55,7 @@ template<uint Size> auto M68K::fetch(EffectiveAddress& ea) -> uint32 {
auto index = extension & 0x8000
? read(AddressRegister{extension >> 12})
: read(DataRegister{extension >> 12});
if(extension & 0x800) index = (int16)index;
if(!(extension & 0x800)) index = (int16)index;
return base + index + (int8)extension;
}

View File

@ -124,8 +124,9 @@ template<uint Size> auto M68K::instructionADDQ(uint4 immediate, EffectiveAddress
write<Size>(with, result);
}
//Size is ignored: always uses Long
template<uint Size> auto M68K::instructionADDQ(uint4 immediate, AddressRegister with) -> void {
auto result = read<Size>(with) + immediate;
auto result = read<Long>(with) + immediate;
write<Long>(with, result);
}
@ -687,7 +688,7 @@ template<uint Size> auto M68K::instructionMOVEP(EffectiveAddress from, DataRegis
}
auto M68K::instructionMOVEQ(DataRegister dr, uint8 immediate) -> void {
write<Long>(dr, immediate);
write<Long>(dr, sign<Byte>(immediate));
r.c = 0;
r.v = 0;
@ -1093,9 +1094,10 @@ template<uint Size> auto M68K::instructionSUBQ(uint4 immediate, EffectiveAddress
write<Size>(with, result);
}
//Size is ignored: always uses Long
template<uint Size> auto M68K::instructionSUBQ(uint4 immediate, AddressRegister with) -> void {
auto result = read<Size>(with) - immediate;
write<Size>(with, result);
auto result = read<Long>(with) - immediate;
write<Long>(with, result);
}
template<uint Size> auto M68K::instructionSUBX(EffectiveAddress with, EffectiveAddress from) -> void {

View File

@ -0,0 +1,19 @@
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
auto Z80::instruction() -> void {
instructionsExecuted++;
r.r = (r.r & 0x80) | (r.r + 1 & 0x7f);
auto opcode = read(r.pc++);
switch(opcode) {
op(0x00, NOP)
op(0xf3, DI)
}
print("[Z80] unimplemented instruction: ", hex(opcode, 2L), "\n");
print("[Z80] instructions executed: ", --instructionsExecuted, "\n");
while(true) wait();
}
#undef op

View File

@ -0,0 +1,5 @@
auto Z80::instructionDI() -> void {
}
auto Z80::instructionNOP() -> void {
}

View File

@ -1,5 +1,25 @@
#include <processor/processor.hpp>
#include "z80.hpp"
namespace Processor {
#include "instruction.cpp"
#include "instructions.cpp"
auto Z80::power() -> void {
}
auto Z80::reset() -> void {
r.af = 0x0000;
r.bc = 0x0000;
r.de = 0x0000;
r.hl = 0x0000;
r.ix = 0x0000;
r.iy = 0x0000;
r.sp = 0x0000;
r.pc = 0x0000;
r.i = 0x00;
r.r = 0x00;
}
}

View File

@ -5,6 +5,63 @@
namespace Processor {
struct Z80 {
virtual auto wait() -> void = 0;
virtual auto read(uint16 addr) -> uint8 = 0;
virtual auto write(uint16 addr, uint8 data) -> void = 0;
//z80.cpp
auto power() -> void;
auto reset() -> void;
//instruction.cpp
auto instruction() -> void;
//instructions.cpp
auto instructionDI() -> void;
auto instructionNOP() -> void;
struct Registers {
union {
uint16_t af;
struct { uint8_t order_msb2(a, f); };
union {
BooleanBitField<uint16_t, 0> c; //carry
BooleanBitField<uint16_t, 1> s; //subtract
BooleanBitField<uint16_t, 2> v; //overflow
//BooleanBitField<uint16_t, 3> _; //unused (copy of bit 3 of result)
BooleanBitField<uint16_t, 4> h; //half-carry
//BooleanBitField<uint16_t, 5> _; //unused (copy of bit 5 of result)
BooleanBitField<uint16_t, 6> z; //zero
BooleanBitField<uint16_t, 7> n; //negative
} p;
};
union {
uint16_t bc;
struct { uint8_t order_msb2(b, c); };
};
union {
uint16_t de;
struct { uint8_t order_msb2(d, e); };
};
union {
uint16_t hl;
struct { uint8_t order_msb2(h, l); };
};
uint16_t ix;
uint16_t iy;
uint16_t sp;
uint16_t pc;
uint8_t i;
uint8_t r;
} r;
private:
uint64 instructionsExecuted = 0;
};
}