mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-18 22:31:33 +02:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e39987a3e3 | ||
|
f5e5bf1772 | ||
|
c50723ef61 | ||
|
ca277cd5e8 | ||
|
306cac2b54 | ||
|
f230d144b5 | ||
|
7ccfbe0206 | ||
|
4b897ba791 | ||
|
be3f6ac0d5 | ||
|
92fe5b0813 | ||
|
059347e575 | ||
|
0d6a09f9f8 | ||
|
b72f35a13e | ||
|
1c0ef793fe | ||
|
76a8ecd32a | ||
|
3dd1aa9c1b | ||
|
88c79e56a0 |
48
higan/emulator/cheat.hpp
Normal file
48
higan/emulator/cheat.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint data;
|
||||
maybe<uint> comp;
|
||||
};
|
||||
|
||||
explicit operator bool() const {
|
||||
return codes.size() > 0;
|
||||
}
|
||||
|
||||
auto reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto append(uint addr, uint data, maybe<uint> comp = nothing) -> void {
|
||||
codes.append({addr, data, comp});
|
||||
}
|
||||
|
||||
auto assign(const string_vector& list) -> void {
|
||||
reset();
|
||||
for(auto& entry : list) {
|
||||
for(auto code : entry.split("+")) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) append(part[0].hex(), part[2].hex(), part[1].hex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (!code.comp || code.comp() == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
private:
|
||||
vector<Code> codes;
|
||||
};
|
||||
|
||||
}
|
13
higan/emulator/debugger.hpp
Normal file
13
higan/emulator/debugger.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#define debug(id, ...) if(debugger.id) debugger.id(__VA_ARGS__)
|
||||
#define privileged public
|
||||
#else
|
||||
#define debug(id, ...)
|
||||
#define privileged private
|
||||
#endif
|
||||
|
||||
}
|
@@ -11,19 +11,21 @@ using namespace nall;
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "100";
|
||||
static const string Version = "101";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "100";
|
||||
static const string SerializerVersion = "101";
|
||||
|
||||
namespace Constants {
|
||||
namespace Colorburst {
|
||||
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "interface.hpp"
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
#define privileged public
|
||||
#else
|
||||
#define privileged private
|
||||
#endif
|
||||
#include "debugger.hpp"
|
||||
|
91
higan/emulator/scheduler.hpp
Normal file
91
higan/emulator/scheduler.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeMaster,
|
||||
SynchronizeSlave,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Step,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
inline auto synchronizing() const -> bool { return _mode == Mode::SynchronizeSlave; }
|
||||
|
||||
auto reset() -> void {
|
||||
_host = co_active();
|
||||
_threads.reset();
|
||||
}
|
||||
|
||||
auto primary(Thread& thread) -> void {
|
||||
_master = _resume = thread.handle();
|
||||
}
|
||||
|
||||
auto append(Thread& thread) -> bool {
|
||||
if(_threads.find(&thread)) return false;
|
||||
thread._clock += _threads.size(); //this bias prioritizes threads appended earlier first
|
||||
return _threads.append(&thread), true;
|
||||
}
|
||||
|
||||
auto remove(Thread& thread) -> bool {
|
||||
if(auto offset = _threads.find(&thread)) return _threads.remove(*offset), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto enter(Mode mode = Mode::Run) -> Event {
|
||||
_mode = mode;
|
||||
_host = co_active();
|
||||
co_switch(_resume);
|
||||
return _event;
|
||||
}
|
||||
|
||||
inline auto resume(Thread& thread) -> void {
|
||||
if(_mode != Mode::SynchronizeSlave) co_switch(thread.handle());
|
||||
}
|
||||
|
||||
auto exit(Event event) -> void {
|
||||
uintmax minimum = -1;
|
||||
for(auto thread : _threads) {
|
||||
if(thread->_clock < minimum) minimum = thread->_clock;
|
||||
}
|
||||
for(auto thread : _threads) {
|
||||
thread->_clock -= minimum;
|
||||
}
|
||||
|
||||
_event = event;
|
||||
_resume = co_active();
|
||||
co_switch(_host);
|
||||
}
|
||||
|
||||
inline auto synchronize(Thread& thread) -> void {
|
||||
if(thread.handle() == _master) {
|
||||
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
|
||||
} else {
|
||||
_resume = thread.handle();
|
||||
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
inline auto synchronize() -> void {
|
||||
if(co_active() == _master) {
|
||||
if(_mode == Mode::SynchronizeMaster) return exit(Event::Synchronize);
|
||||
} else {
|
||||
if(_mode == Mode::SynchronizeSlave) return exit(Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cothread_t _host = nullptr; //program thread (used to exit scheduler)
|
||||
cothread_t _resume = nullptr; //resume thread (used to enter scheduler)
|
||||
cothread_t _master = nullptr; //primary thread (used to synchronize components)
|
||||
Mode _mode = Mode::Run;
|
||||
Event _event = Event::Step;
|
||||
vector<Thread*> _threads;
|
||||
};
|
||||
|
||||
}
|
54
higan/emulator/thread.hpp
Normal file
54
higan/emulator/thread.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Thread {
|
||||
virtual ~Thread() {
|
||||
if(_handle) co_delete(_handle);
|
||||
}
|
||||
|
||||
inline auto active() const { return co_active() == _handle; }
|
||||
inline auto handle() const { return _handle; }
|
||||
inline auto frequency() const { return _frequency; }
|
||||
inline auto scalar() const { return _scalar; }
|
||||
inline auto clock() const { return _clock; }
|
||||
|
||||
auto setFrequency(double frequency) -> void {
|
||||
_frequency = frequency + 0.5;
|
||||
_scalar = ((uintmax)1 << (8 * sizeof(uintmax) - 1)) / _frequency;
|
||||
}
|
||||
|
||||
auto setScalar(uintmax scalar) -> void {
|
||||
_scalar = scalar;
|
||||
}
|
||||
|
||||
auto setClock(uintmax clock) -> void {
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
if(_handle) co_delete(_handle);
|
||||
_handle = co_create(64 * 1024 * sizeof(void*), entrypoint);
|
||||
setFrequency(frequency);
|
||||
}
|
||||
|
||||
inline auto step(uint clocks) -> void {
|
||||
_clock += _scalar * clocks;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(_frequency);
|
||||
s.integer(_scalar);
|
||||
s.integer(_clock);
|
||||
}
|
||||
|
||||
protected:
|
||||
cothread_t _handle = nullptr;
|
||||
uintmax _frequency = 0;
|
||||
uintmax _scalar = 0;
|
||||
uintmax _clock = 0;
|
||||
|
||||
friend class Scheduler;
|
||||
};
|
||||
|
||||
}
|
@@ -1,16 +1,13 @@
|
||||
processors += r6502
|
||||
|
||||
objects += fc-interface fc-system fc-scheduler fc-controller
|
||||
objects += fc-interface fc-system fc-controller
|
||||
objects += fc-memory fc-cartridge fc-cpu fc-apu fc-ppu
|
||||
objects += fc-cheat
|
||||
|
||||
obj/fc-interface.o: fc/interface/interface.cpp $(call rwildcard,fc/interface/)
|
||||
obj/fc-system.o: fc/system/system.cpp $(call rwildcard,fc/system/)
|
||||
obj/fc-scheduler.o: fc/scheduler/scheduler.cpp $(call rwildcard,fc/scheduler/)
|
||||
obj/fc-controller.o: fc/controller/controller.cpp $(call rwildcard,fc/controller/)
|
||||
obj/fc-memory.o: fc/memory/memory.cpp $(call rwildcard,fc/memory/)
|
||||
obj/fc-cartridge.o: fc/cartridge/cartridge.cpp $(call rwildcard,fc/cartridge/)
|
||||
obj/fc-cpu.o: fc/cpu/cpu.cpp $(call rwildcard,fc/cpu/)
|
||||
obj/fc-apu.o: fc/apu/apu.cpp $(call rwildcard,fc/apu/)
|
||||
obj/fc-ppu.o: fc/ppu/ppu.cpp $(call rwildcard,fc/ppu/)
|
||||
obj/fc-cheat.o: fc/cheat/cheat.cpp $(call rwildcard,fc/cheat/)
|
||||
|
@@ -63,8 +63,8 @@ auto APU::main() -> void {
|
||||
}
|
||||
|
||||
auto APU::tick() -> void {
|
||||
clock += 12;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(12);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto APU::setIRQ() -> void {
|
||||
@@ -88,8 +88,8 @@ auto APU::power() -> void {
|
||||
}
|
||||
|
||||
auto APU::reset() -> void {
|
||||
create(APU::Enter, 21'477'272);
|
||||
stream = Emulator::audio.createStream(1, 21'477'272.0 / 12.0);
|
||||
create(APU::Enter, system.colorburst() * 6.0);
|
||||
stream = Emulator::audio.createStream(1, system.colorburst() / 2.0);
|
||||
|
||||
pulse[0].reset();
|
||||
pulse[1].reset();
|
||||
|
@@ -109,13 +109,13 @@ auto Board::mirror(uint addr, uint size) -> uint {
|
||||
}
|
||||
|
||||
auto Board::main() -> void {
|
||||
cartridge.clock += 12 * 4095;
|
||||
cartridge.step(12 * 4095);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto Board::tick() -> void {
|
||||
cartridge.clock += 12;
|
||||
if(cartridge.clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
cartridge.step(12);
|
||||
cartridge.synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Board::readCHR(uint addr) -> uint8 {
|
||||
|
@@ -49,7 +49,7 @@ auto Cartridge::power() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::reset() -> void {
|
||||
create(Cartridge::Enter, 21'477'272);
|
||||
create(Cartridge::Enter, system.colorburst() * 6.0);
|
||||
board->reset();
|
||||
}
|
||||
|
||||
|
@@ -1,28 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
@@ -5,22 +5,24 @@ namespace Famicom {
|
||||
#include "gamepad/gamepad.cpp"
|
||||
|
||||
Controller::Controller(bool port) : port(port) {
|
||||
if(!thread) create(Controller::Enter, 1);
|
||||
if(!handle()) create(Controller::Enter, 1);
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
||||
if(peripherals.controllerPort1->active()) peripherals.controllerPort1->main();
|
||||
if(peripherals.controllerPort2->active()) peripherals.controllerPort2->main();
|
||||
}
|
||||
}
|
||||
|
||||
auto Controller::main() -> void {
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@
|
||||
// 6: data4 $4016.d4 read $4017.d4 read
|
||||
// 7: gnd
|
||||
|
||||
struct Controller : Cothread {
|
||||
struct Controller : Thread {
|
||||
enum : bool { Port1 = 0, Port2 = 1 };
|
||||
|
||||
Controller(bool port);
|
||||
|
@@ -17,19 +17,11 @@ auto CPU::main() -> void {
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
cartridge.clock -= clocks;
|
||||
if(cartridge.clock < 0) co_switch(cartridge.thread);
|
||||
|
||||
for(auto peripheral : peripherals) {
|
||||
peripheral->clock -= clocks * (uint64)peripheral->frequency;
|
||||
if(peripheral->clock < 0) co_switch(peripheral->thread);
|
||||
}
|
||||
Thread::step(clocks);
|
||||
synchronize(apu);
|
||||
synchronize(ppu);
|
||||
synchronize(cartridge);
|
||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
@@ -44,7 +36,7 @@ auto CPU::power() -> void {
|
||||
|
||||
auto CPU::reset() -> void {
|
||||
R6502::reset();
|
||||
create(CPU::Enter, 21'477'272);
|
||||
create(CPU::Enter, system.colorburst() * 6.0);
|
||||
|
||||
regs.pc = bus.read(0xfffc) << 0;
|
||||
regs.pc |= bus.read(0xfffd) << 8;
|
||||
|
@@ -4,39 +4,30 @@
|
||||
//started: 2011-09-05
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/r6502/r6502.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
using File = Emulator::File;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
inline auto synchronize(Thread& thread) -> void {
|
||||
if(clock() >= thread.clock()) scheduler.resume(thread);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
|
||||
struct Cothread : Thread {
|
||||
auto step(uint clocks) -> void;
|
||||
auto synchronizeCPU() -> void;
|
||||
};
|
||||
|
||||
#include <fc/scheduler/scheduler.hpp>
|
||||
#include <fc/controller/controller.hpp>
|
||||
#include <fc/system/system.hpp>
|
||||
#include <fc/memory/memory.hpp>
|
||||
@@ -44,16 +35,6 @@ namespace Famicom {
|
||||
#include <fc/cpu/cpu.hpp>
|
||||
#include <fc/apu/apu.hpp>
|
||||
#include <fc/ppu/ppu.hpp>
|
||||
#include <fc/cheat/cheat.hpp>
|
||||
|
||||
inline auto Cothread::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
synchronizeCPU();
|
||||
}
|
||||
|
||||
inline auto Cothread::synchronizeCPU() -> void {
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
}
|
||||
}
|
||||
|
||||
#include <fc/interface/interface.hpp>
|
||||
|
@@ -164,15 +164,7 @@ auto Interface::unserialize(serializer& s) -> bool {
|
||||
}
|
||||
|
||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
||||
cheat.reset();
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
|
@@ -17,7 +17,7 @@ auto Bus::read(uint16 addr) -> uint8 {
|
||||
else if(addr <= 0x3fff) data = ppu.readIO(addr);
|
||||
else if(addr <= 0x4017) data = cpu.readIO(addr);
|
||||
|
||||
if(cheat.enable()) {
|
||||
if(cheat) {
|
||||
if(auto result = cheat.find(addr, data)) return result();
|
||||
}
|
||||
|
||||
|
@@ -27,8 +27,8 @@ auto PPU::step(uint clocks) -> void {
|
||||
if(io.ly == 261 && io.lx == 0) io.nmiFlag = io.nmiHold;
|
||||
if(io.ly == 261 && io.lx == 2) cpu.nmiLine(io.nmiEnable && io.nmiFlag);
|
||||
|
||||
clock += 4;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(4);
|
||||
synchronize(cpu);
|
||||
|
||||
io.lx++;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ auto PPU::power() -> void {
|
||||
}
|
||||
|
||||
auto PPU::reset() -> void {
|
||||
create(PPU::Enter, 21'477'272);
|
||||
create(PPU::Enter, system.colorburst() * 6.0);
|
||||
|
||||
memory::fill(&io, sizeof(IO));
|
||||
memory::fill(&latch, sizeof(Latches));
|
||||
|
@@ -1,44 +0,0 @@
|
||||
#include <fc/fc.hpp>
|
||||
|
||||
namespace Famicom {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::reset() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto reset() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -6,19 +6,19 @@ namespace Famicom {
|
||||
#include "video.cpp"
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(cartridge.thread);
|
||||
for(auto peripheral : cpu.peripherals) {
|
||||
scheduler.synchronize(peripheral->thread);
|
||||
}
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(apu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(cartridge);
|
||||
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
@@ -30,6 +30,7 @@ auto System::load() -> bool {
|
||||
}
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
if(!cartridge.load()) return false;
|
||||
information.colorburst = Emulator::Constants::Colorburst::NTSC;
|
||||
serializeInit();
|
||||
return information.loaded = true;
|
||||
}
|
||||
@@ -62,11 +63,12 @@ auto System::reset() -> void {
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
cartridge.reset();
|
||||
cpu.reset();
|
||||
apu.reset();
|
||||
ppu.reset();
|
||||
scheduler.reset();
|
||||
scheduler.primary(cpu);
|
||||
peripherals.reset();
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
struct System {
|
||||
auto loaded() const -> bool { return information.loaded; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
@@ -27,6 +28,7 @@ struct System {
|
||||
|
||||
struct Information {
|
||||
bool loaded = false;
|
||||
double colorburst = 0.0;
|
||||
string manifest;
|
||||
} information;
|
||||
|
||||
|
@@ -1,16 +1,13 @@
|
||||
processors += lr35902
|
||||
|
||||
objects += gb-interface gb-system gb-scheduler
|
||||
objects += gb-interface gb-system
|
||||
objects += gb-memory gb-cartridge
|
||||
objects += gb-cpu gb-ppu gb-apu
|
||||
objects += gb-cheat
|
||||
|
||||
obj/gb-interface.o: gb/interface/interface.cpp $(call rwildcard,gb/interface/)
|
||||
obj/gb-system.o: gb/system/system.cpp $(call rwildcard,gb/system/)
|
||||
obj/gb-scheduler.o: gb/scheduler/scheduler.cpp $(call rwildcard,gb/scheduler/)
|
||||
obj/gb-cartridge.o: gb/cartridge/cartridge.cpp $(call rwildcard,gb/cartridge/)
|
||||
obj/gb-memory.o: gb/memory/memory.cpp $(call rwildcard,gb/memory/)
|
||||
obj/gb-cpu.o: gb/cpu/cpu.cpp $(call rwildcard,gb/cpu/)
|
||||
obj/gb-ppu.o: gb/ppu/ppu.cpp $(call rwildcard,gb/ppu/)
|
||||
obj/gb-apu.o: gb/apu/apu.cpp $(call rwildcard,gb/apu/)
|
||||
obj/gb-cheat.o: gb/cheat/cheat.cpp $(call rwildcard,gb/cheat/)
|
||||
|
@@ -51,8 +51,8 @@ auto APU::main() -> void {
|
||||
}
|
||||
cycle++;
|
||||
|
||||
clock += cpu.frequency;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
//filter to remove DC bias
|
||||
|
@@ -1,28 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
struct Cheat {
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
@@ -77,8 +77,8 @@ auto CPU::stop() -> bool {
|
||||
if(status.speedSwitch) {
|
||||
status.speedSwitch = 0;
|
||||
status.speedDouble ^= 1;
|
||||
if(status.speedDouble == 0) frequency = 4 * 1024 * 1024;
|
||||
if(status.speedDouble == 1) frequency = 8 * 1024 * 1024;
|
||||
if(status.speedDouble == 0) setFrequency(4 * 1024 * 1024);
|
||||
if(status.speedDouble == 1) setFrequency(8 * 1024 * 1024);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@@ -16,11 +16,9 @@ auto CPU::step(uint clocks) -> void {
|
||||
if((status.div & 511) == 0) timer8192hz();
|
||||
if((status.div & 1023) == 0) timer4096hz();
|
||||
|
||||
ppu.clock -= ppu.frequency;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= apu.frequency;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
Thread::step(1);
|
||||
synchronize(ppu);
|
||||
synchronize(apu);
|
||||
}
|
||||
|
||||
if(system.sgb()) {
|
||||
|
@@ -4,41 +4,36 @@
|
||||
//started: 2010-12-27
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/lr35902/lr35902.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
using File = Emulator::File;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
inline auto synchronize(Thread& thread) -> void {
|
||||
if(clock() >= thread.clock()) scheduler.resume(thread);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
};
|
||||
|
||||
#include <gb/memory/memory.hpp>
|
||||
#include <gb/system/system.hpp>
|
||||
#include <gb/scheduler/scheduler.hpp>
|
||||
#include <gb/cartridge/cartridge.hpp>
|
||||
#include <gb/cpu/cpu.hpp>
|
||||
#include <gb/ppu/ppu.hpp>
|
||||
#include <gb/apu/apu.hpp>
|
||||
#include <gb/cheat/cheat.hpp>
|
||||
}
|
||||
|
||||
#include <gb/interface/interface.hpp>
|
||||
|
@@ -163,15 +163,7 @@ auto Interface::unserialize(serializer& s) -> bool {
|
||||
}
|
||||
|
||||
auto Interface::cheatSet(const string_vector& list) -> void {
|
||||
cheat.reset();
|
||||
for(auto& codeset : list) {
|
||||
auto codes = codeset.split("+");
|
||||
for(auto& code : codes) {
|
||||
auto part = code.split("/");
|
||||
if(part.size() == 2) cheat.append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) cheat.append(part[0].hex(), part[1].hex(), part[2].hex());
|
||||
}
|
||||
}
|
||||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::lcdScanline() -> void {
|
||||
|
@@ -38,7 +38,7 @@ auto Memory::free() -> void {
|
||||
auto Bus::read(uint16 addr) -> uint8 {
|
||||
uint8 data = mmio[addr]->readIO(addr);
|
||||
|
||||
if(cheat.enable()) {
|
||||
if(cheat) {
|
||||
if(auto result = cheat.find(addr, data)) return result();
|
||||
}
|
||||
|
||||
|
@@ -118,9 +118,9 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
||||
status.lx = 0;
|
||||
|
||||
//restart cothread to begin new frame
|
||||
auto clock = this->clock;
|
||||
auto clock = Thread::clock();
|
||||
create(Enter, 4 * 1024 * 1024);
|
||||
this->clock = clock;
|
||||
Thread::setClock(clock);
|
||||
}
|
||||
|
||||
status.displayEnable = data & 0x80;
|
||||
|
@@ -94,8 +94,8 @@ auto PPU::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
status.lx++;
|
||||
clock += cpu.frequency;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,44 +0,0 @@
|
||||
#include <gb/gb.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Step,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto power() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -5,15 +5,17 @@ namespace GameBoy {
|
||||
#include "video.cpp"
|
||||
#include "serialization.cpp"
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Cheat cheat;
|
||||
|
||||
auto System::run() -> void {
|
||||
scheduler.enter();
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(apu);
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
@@ -66,12 +68,13 @@ auto System::power() -> void {
|
||||
Emulator::audio.setInterface(interface);
|
||||
}
|
||||
|
||||
scheduler.reset();
|
||||
bus.power();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
scheduler.power();
|
||||
scheduler.primary(cpu);
|
||||
|
||||
_clocksExecuted = 0;
|
||||
}
|
||||
|
@@ -1,12 +1,11 @@
|
||||
processors += arm
|
||||
|
||||
objects += gba-memory gba-interface gba-scheduler gba-system
|
||||
objects += gba-memory gba-interface gba-system
|
||||
objects += gba-cartridge gba-player
|
||||
objects += gba-cpu gba-ppu gba-apu
|
||||
|
||||
obj/gba-memory.o: gba/memory/memory.cpp $(call rwildcard,gba/memory)
|
||||
obj/gba-interface.o: gba/interface/interface.cpp $(call rwildcard,gba/interface)
|
||||
obj/gba-scheduler.o: gba/scheduler/scheduler.cpp $(call rwildcard,gba/scheduler)
|
||||
obj/gba-system.o: gba/system/system.cpp $(call rwildcard,gba/system)
|
||||
obj/gba-cartridge.o: gba/cartridge/cartridge.cpp $(call rwildcard,gba/cartridge)
|
||||
obj/gba-player.o: gba/player/player.cpp $(call rwildcard,gba/player)
|
||||
|
@@ -68,8 +68,8 @@ auto APU::main() -> void {
|
||||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
|
@@ -83,11 +83,9 @@ auto CPU::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto CPU::syncStep(uint clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(ppu);
|
||||
synchronize(apu);
|
||||
}
|
||||
|
||||
auto CPU::keypadRun() -> void {
|
||||
|
@@ -4,10 +4,15 @@
|
||||
//started: 2012-03-19
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
|
||||
#include <processor/arm/arm.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
using File = Emulator::File;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
extern Scheduler scheduler;
|
||||
|
||||
enum : uint { //mode flags for bus read, write:
|
||||
Nonsequential = 1, //N cycle
|
||||
@@ -21,30 +26,22 @@ namespace GameBoyAdvance {
|
||||
Signed = 256, //sign extended
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65'536 * sizeof(void*), entrypoint);
|
||||
this->frequency = frequency;
|
||||
clock = 0;
|
||||
inline auto synchronize(Thread& thread) -> void {
|
||||
if(clock() >= thread.clock()) scheduler.resume(thread);
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
inline auto step(uint clocks) -> void {
|
||||
_clock += clocks;
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int clock = 0;
|
||||
};
|
||||
|
||||
#include <gba/memory/memory.hpp>
|
||||
#include <gba/scheduler/scheduler.hpp>
|
||||
#include <gba/system/system.hpp>
|
||||
#include <gba/cartridge/cartridge.hpp>
|
||||
#include <gba/player/player.hpp>
|
||||
|
@@ -43,8 +43,8 @@ auto PPU::main() -> void {
|
||||
}
|
||||
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto PPU::power() -> void {
|
||||
|
@@ -1,44 +0,0 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
auto Scheduler::power() -> void {
|
||||
host = co_active();
|
||||
resume = cpu.thread;
|
||||
}
|
||||
|
||||
auto Scheduler::enter(Mode mode_) -> Event {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
if(event == Event::Frame) ppu.refresh();
|
||||
return event;
|
||||
}
|
||||
|
||||
auto Scheduler::exit(Event event_) -> void {
|
||||
event = event_;
|
||||
resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize(cothread_t thread) -> void {
|
||||
if(thread == cpu.thread) {
|
||||
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
||||
auto Scheduler::synchronize() -> void {
|
||||
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
|
||||
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
|
||||
}
|
||||
|
||||
auto Scheduler::synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeAll;
|
||||
}
|
||||
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
SynchronizeCPU,
|
||||
SynchronizeAll,
|
||||
};
|
||||
|
||||
enum class Event : uint {
|
||||
Unknown,
|
||||
Frame,
|
||||
Synchronize,
|
||||
};
|
||||
|
||||
auto power() -> void;
|
||||
auto enter(Mode = Mode::Run) -> Event;
|
||||
auto exit(Event) -> void;
|
||||
auto synchronize(cothread_t) -> void;
|
||||
auto synchronize() -> void;
|
||||
auto synchronizing() const -> bool;
|
||||
|
||||
private:
|
||||
cothread_t host = nullptr;
|
||||
cothread_t resume = nullptr;
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Unknown;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
@@ -7,6 +7,7 @@ namespace GameBoyAdvance {
|
||||
#include "serialization.cpp"
|
||||
BIOS bios;
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
|
||||
auto System::init() -> void {
|
||||
}
|
||||
@@ -23,13 +24,14 @@ auto System::power() -> void {
|
||||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
bus.power();
|
||||
player.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.power();
|
||||
scheduler.primary(cpu);
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
@@ -61,13 +63,13 @@ auto System::unload() -> void {
|
||||
}
|
||||
|
||||
auto System::run() -> void {
|
||||
while(scheduler.enter() != Scheduler::Event::Frame);
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) ppu.refresh();
|
||||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(apu);
|
||||
}
|
||||
|
||||
}
|
||||
|
14
higan/md/GNUmakefile
Normal file
14
higan/md/GNUmakefile
Normal file
@@ -0,0 +1,14 @@
|
||||
processors += m68k z80
|
||||
|
||||
objects += md-interface
|
||||
objects += md-cpu md-apu md-vdp md-psg md-ym2612
|
||||
objects += md-system md-cartridge
|
||||
|
||||
obj/md-interface.o: md/interface/interface.cpp $(call rwildcard,md/interface)
|
||||
obj/md-cpu.o: md/cpu/cpu.cpp $(call rwildcard,md/cpu)
|
||||
obj/md-apu.o: md/apu/apu.cpp $(call rwildcard,md/apu)
|
||||
obj/md-vdp.o: md/vdp/vdp.cpp $(call rwildcard,md/vdp)
|
||||
obj/md-psg.o: md/psg/psg.cpp $(call rwildcard,md/psg)
|
||||
obj/md-ym2612.o: md/ym2612/ym2612.cpp $(call rwildcard,md/ym2612)
|
||||
obj/md-system.o: md/system/system.cpp $(call rwildcard,md/system)
|
||||
obj/md-cartridge.o: md/cartridge/cartridge.cpp $(call rwildcard,md/cartridge)
|
25
higan/md/apu/apu.cpp
Normal file
25
higan/md/apu/apu.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
APU apu;
|
||||
|
||||
auto APU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), apu.main();
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
step(system.colorburst());
|
||||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
}
|
||||
|
||||
auto APU::reset() -> void {
|
||||
create(APU::Enter, system.colorburst());
|
||||
}
|
||||
|
||||
}
|
12
higan/md/apu/apu.hpp
Normal file
12
higan/md/apu/apu.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
//Zilog Z80
|
||||
|
||||
struct APU : Processor::Z80, Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern APU apu;
|
82
higan/md/cartridge/cartridge.cpp
Normal file
82
higan/md/cartridge/cartridge.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
Cartridge cartridge;
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
information = Information();
|
||||
|
||||
if(auto pathID = interface->load(ID::MegaDrive, "Mega Drive", "md")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
|
||||
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 = Memory();
|
||||
ram = Memory();
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::reset() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::read(bool word, uint24 addr) -> uint16 {
|
||||
uint16 data = rom.data[addr & rom.mask];
|
||||
if(!word) return data;
|
||||
return data << 8 | rom.data[addr + 1 & rom.mask];
|
||||
}
|
||||
|
||||
auto Cartridge::write(bool word, uint24 addr, uint16 data) -> void {
|
||||
}
|
||||
|
||||
}
|
33
higan/md/cartridge/cartridge.hpp
Normal file
33
higan/md/cartridge/cartridge.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
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 power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto read(bool word, uint24 addr) -> uint16;
|
||||
auto write(bool word, uint24 addr, uint16 data) -> void;
|
||||
|
||||
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;
|
63
higan/md/cpu/cpu.cpp
Normal file
63
higan/md/cpu/cpu.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
CPU cpu;
|
||||
|
||||
auto CPU::Enter() -> void {
|
||||
cpu.boot();
|
||||
while(true) scheduler.synchronize(), cpu.main();
|
||||
}
|
||||
|
||||
auto CPU::boot() -> void {
|
||||
r.a[7] = read(1, 0) << 16 | read(1, 2) << 0;
|
||||
r.pc = read(1, 4) << 16 | read(1, 6) << 0;
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
cycles += clocks;
|
||||
if(cycles >= frequency() / 60) {
|
||||
cycles = 0;
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
M68K::power();
|
||||
|
||||
for(auto& byte : ram) byte = 0x00;
|
||||
}
|
||||
|
||||
auto CPU::reset() -> void {
|
||||
M68K::reset();
|
||||
create(CPU::Enter, system.colorburst() * 15.0 / 7.0);
|
||||
cycles = 0;
|
||||
}
|
||||
|
||||
auto CPU::read(bool word, uint24 addr) -> uint16 {
|
||||
if(addr < 0x400000) return cartridge.read(word, addr);
|
||||
if(addr < 0xe00000) return 0x0000;
|
||||
|
||||
uint16 data = ram[addr & 65535];
|
||||
if(word) data = data << 8 | ram[addr + 1 & 65535];
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::write(bool word, uint24 addr, uint16 data) -> void {
|
||||
if(addr < 0x400000) return cartridge.write(word, addr, data);
|
||||
if(addr < 0xe00000) return;
|
||||
|
||||
if(!word) {
|
||||
ram[addr & 65535] = data;
|
||||
} else {
|
||||
ram[addr + 0 & 65535] = data >> 8;
|
||||
ram[addr + 1 & 65535] = data >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
21
higan/md/cpu/cpu.hpp
Normal file
21
higan/md/cpu/cpu.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
//Motorola 68000
|
||||
|
||||
struct CPU : Processor::M68K, Thread {
|
||||
static auto Enter() -> void;
|
||||
auto boot() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void override;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto read(bool word, uint24 addr) -> uint16 override;
|
||||
auto write(bool word, uint24 addr, uint16 data) -> void override;
|
||||
|
||||
private:
|
||||
uint8 ram[64 * 1024];
|
||||
|
||||
uint cycles = 0;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
127
higan/md/interface/interface.cpp
Normal file
127
higan/md/interface/interface.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
Interface* interface = nullptr;
|
||||
Settings settings;
|
||||
|
||||
Interface::Interface() {
|
||||
interface = this;
|
||||
|
||||
information.manufacturer = "Sega";
|
||||
information.name = "Mega Drive";
|
||||
information.width = 320; //1280
|
||||
information.height = 240; // 480
|
||||
information.overscan = true;
|
||||
information.aspectRatio = 4.0 / 3.0;
|
||||
information.resettable = true;
|
||||
|
||||
information.capability.states = false;
|
||||
information.capability.cheats = false;
|
||||
|
||||
media.append({ID::MegaDrive, "Mega Drive", "md"});
|
||||
|
||||
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
|
||||
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
|
||||
|
||||
{ Device device{ID::Device::Gamepad, "Gamepad"};
|
||||
device.inputs.append({0, "Up" });
|
||||
device.inputs.append({0, "Down" });
|
||||
device.inputs.append({0, "Left" });
|
||||
device.inputs.append({0, "Right"});
|
||||
device.inputs.append({0, "A" });
|
||||
device.inputs.append({0, "B" });
|
||||
device.inputs.append({0, "C" });
|
||||
device.inputs.append({0, "X" });
|
||||
device.inputs.append({0, "Y" });
|
||||
device.inputs.append({0, "Z" });
|
||||
device.inputs.append({0, "Start"});
|
||||
controllerPort1.devices.append(device);
|
||||
controllerPort2.devices.append(device);
|
||||
}
|
||||
|
||||
ports.append(move(controllerPort1));
|
||||
ports.append(move(controllerPort2));
|
||||
}
|
||||
|
||||
auto Interface::manifest() -> string {
|
||||
return cartridge.manifest();
|
||||
}
|
||||
|
||||
auto Interface::title() -> string {
|
||||
return cartridge.title();
|
||||
}
|
||||
|
||||
auto Interface::videoFrequency() -> double {
|
||||
return 60.0;
|
||||
}
|
||||
|
||||
auto Interface::videoColors() -> uint32 {
|
||||
return 1 << 9;
|
||||
}
|
||||
|
||||
auto Interface::videoColor(uint32 color) -> uint64 {
|
||||
uint B = color.bits(0,2);
|
||||
uint G = color.bits(3,5);
|
||||
uint R = color.bits(6,8);
|
||||
|
||||
uint64 r = image::normalize(R, 3, 16);
|
||||
uint64 g = image::normalize(G, 3, 16);
|
||||
uint64 b = image::normalize(B, 3, 16);
|
||||
|
||||
return r << 32 | g << 16 | b << 0;
|
||||
}
|
||||
|
||||
auto Interface::audioFrequency() -> double {
|
||||
return 52'000.0;
|
||||
}
|
||||
|
||||
auto Interface::loaded() -> bool {
|
||||
return system.loaded();
|
||||
}
|
||||
|
||||
auto Interface::load(uint id) -> bool {
|
||||
return system.load();
|
||||
}
|
||||
|
||||
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 {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Interface::unserialize(serializer& s) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::get(const string& name) -> any {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Interface::set(const string& name, const any& value) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
54
higan/md/interface/interface.hpp
Normal file
54
higan/md/interface/interface.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace MegaDrive {
|
||||
|
||||
struct ID {
|
||||
enum : uint {
|
||||
System,
|
||||
MegaDrive,
|
||||
};
|
||||
|
||||
struct Port { enum : uint {
|
||||
Controller1,
|
||||
Controller2,
|
||||
};};
|
||||
|
||||
struct Device { enum : uint {
|
||||
Gamepad,
|
||||
};};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
using Emulator::Interface::load;
|
||||
|
||||
Interface();
|
||||
|
||||
auto manifest() -> string override;
|
||||
auto title() -> string override;
|
||||
auto videoFrequency() -> double override;
|
||||
auto videoColors() -> uint32 override;
|
||||
auto videoColor(uint32 color) -> uint64 override;
|
||||
auto audioFrequency() -> double override;
|
||||
|
||||
auto loaded() -> bool override;
|
||||
auto load(uint id) -> bool override;
|
||||
auto save() -> void override;
|
||||
auto unload() -> void override;
|
||||
|
||||
auto power() -> void override;
|
||||
auto reset() -> void override;
|
||||
auto run() -> void override;
|
||||
|
||||
auto serialize() -> serializer override;
|
||||
auto unserialize(serializer&) -> bool override;
|
||||
|
||||
auto cap(const string& name) -> bool override;
|
||||
auto get(const string& name) -> any override;
|
||||
auto set(const string& name, const any& value) -> bool override;
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
};
|
||||
|
||||
extern Interface* interface;
|
||||
extern Settings settings;
|
||||
|
||||
}
|
39
higan/md/md.hpp
Normal file
39
higan/md/md.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
//license: GPLv3
|
||||
//started: 2016-07-08
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
|
||||
#include <processor/m68k/m68k.hpp>
|
||||
#include <processor/z80/z80.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
using File = Emulator::File;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
extern Scheduler scheduler;
|
||||
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
inline auto synchronize(Thread& thread) -> void {
|
||||
if(clock() >= thread.clock()) scheduler.resume(thread);
|
||||
}
|
||||
};
|
||||
|
||||
#include <md/cpu/cpu.hpp>
|
||||
#include <md/apu/apu.hpp>
|
||||
#include <md/vdp/vdp.hpp>
|
||||
#include <md/psg/psg.hpp>
|
||||
#include <md/ym2612/ym2612.hpp>
|
||||
|
||||
#include <md/system/system.hpp>
|
||||
#include <md/cartridge/cartridge.hpp>
|
||||
}
|
||||
|
||||
#include <md/interface/interface.hpp>
|
25
higan/md/psg/psg.cpp
Normal file
25
higan/md/psg/psg.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
PSG psg;
|
||||
|
||||
auto PSG::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), psg.main();
|
||||
}
|
||||
|
||||
auto PSG::main() -> void {
|
||||
step(system.colorburst());
|
||||
}
|
||||
|
||||
auto PSG::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto PSG::power() -> void {
|
||||
}
|
||||
|
||||
auto PSG::reset() -> void {
|
||||
create(PSG::Enter, system.colorburst());
|
||||
}
|
||||
|
||||
}
|
12
higan/md/psg/psg.hpp
Normal file
12
higan/md/psg/psg.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
//TI SN76489
|
||||
|
||||
struct PSG : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern PSG psg;
|
62
higan/md/system/system.cpp
Normal file
62
higan/md/system/system.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
|
||||
auto System::run() -> void {
|
||||
if(scheduler.enter() == Scheduler::Event::Frame) {
|
||||
static uint32 output[320 * 240] = {0};
|
||||
Emulator::video.refresh(output, 320 * sizeof(uint32), 320, 240);
|
||||
}
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
information = Information();
|
||||
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();
|
||||
apu.power();
|
||||
vdp.power();
|
||||
psg.power();
|
||||
ym2612.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();
|
||||
apu.reset();
|
||||
vdp.reset();
|
||||
psg.reset();
|
||||
ym2612.reset();
|
||||
scheduler.primary(cpu);
|
||||
}
|
||||
|
||||
}
|
20
higan/md/system/system.hpp
Normal file
20
higan/md/system/system.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
struct System {
|
||||
auto loaded() const -> bool { return information.loaded; }
|
||||
auto colorburst() const -> double { return information.colorburst; }
|
||||
|
||||
auto run() -> void;
|
||||
|
||||
auto load() -> bool;
|
||||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
struct Information {
|
||||
bool loaded = false;
|
||||
string manifest;
|
||||
double colorburst = 0.0;
|
||||
} information;
|
||||
};
|
||||
|
||||
extern System system;
|
28
higan/md/vdp/vdp.cpp
Normal file
28
higan/md/vdp/vdp.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
//256-width = colorburst * 15 / 10
|
||||
//320-width = colorburst * 15 / 8
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
VDP vdp;
|
||||
|
||||
auto VDP::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), vdp.main();
|
||||
}
|
||||
|
||||
auto VDP::main() -> void {
|
||||
step(system.colorburst() * 15.0 / 10.0);
|
||||
}
|
||||
|
||||
auto VDP::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto VDP::power() -> void {
|
||||
}
|
||||
|
||||
auto VDP::reset() -> void {
|
||||
create(VDP::Enter, system.colorburst() * 15.0 / 10.0);
|
||||
}
|
||||
|
||||
}
|
12
higan/md/vdp/vdp.hpp
Normal file
12
higan/md/vdp/vdp.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
//Yamaha YM7101
|
||||
|
||||
struct VDP : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern VDP vdp;
|
25
higan/md/ym2612/ym2612.cpp
Normal file
25
higan/md/ym2612/ym2612.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <md/md.hpp>
|
||||
|
||||
namespace MegaDrive {
|
||||
|
||||
YM2612 ym2612;
|
||||
|
||||
auto YM2612::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), ym2612.main();
|
||||
}
|
||||
|
||||
auto YM2612::main() -> void {
|
||||
step(system.colorburst() * 15.0 / 7.0);
|
||||
}
|
||||
|
||||
auto YM2612::step(uint clocks) -> void {
|
||||
}
|
||||
|
||||
auto YM2612::power() -> void {
|
||||
}
|
||||
|
||||
auto YM2612::reset() -> void {
|
||||
create(YM2612::Enter, system.colorburst() * 15.0 / 7.0);
|
||||
}
|
||||
|
||||
}
|
12
higan/md/ym2612/ym2612.hpp
Normal file
12
higan/md/ym2612/ym2612.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
//Yamaha YM2612
|
||||
|
||||
struct YM2612 : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
};
|
||||
|
||||
extern YM2612 ym2612;
|
@@ -4,18 +4,22 @@ objects += $(if $(findstring arm,$(processors)),processor-arm)
|
||||
objects += $(if $(findstring gsu,$(processors)),processor-gsu)
|
||||
objects += $(if $(findstring hg51b,$(processors)),processor-hg51b)
|
||||
objects += $(if $(findstring lr35902,$(processors)),processor-lr35902)
|
||||
objects += $(if $(findstring m68k,$(processors)),processor-m68k)
|
||||
objects += $(if $(findstring r6502,$(processors)),processor-r6502)
|
||||
objects += $(if $(findstring r65816,$(processors)),processor-r65816)
|
||||
objects += $(if $(findstring spc700,$(processors)),processor-spc700)
|
||||
objects += $(if $(findstring upd96050,$(processors)),processor-upd96050)
|
||||
objects += $(if $(findstring v30mz,$(processors)),processor-v30mz)
|
||||
objects += $(if $(findstring z80,$(processors)),processor-z80)
|
||||
|
||||
obj/processor-arm.o: processor/arm/arm.cpp $(call rwildcard,processor/arm)
|
||||
obj/processor-gsu.o: processor/gsu/gsu.cpp $(call rwildcard,processor/gsu)
|
||||
obj/processor-hg51b.o: processor/hg51b/hg51b.cpp $(call rwildcard,processor/hg51b)
|
||||
obj/processor-lr35902.o: processor/lr35902/lr35902.cpp $(call rwildcard,processor/lr35902)
|
||||
obj/processor-m68k.o: processor/m68k/m68k.cpp $(call rwildcard,processor/m68k)
|
||||
obj/processor-r6502.o: processor/r6502/r6502.cpp $(call rwildcard,processor/r6502)
|
||||
obj/processor-r65816.o: processor/r65816/r65816.cpp $(call rwildcard,processor/r65816)
|
||||
obj/processor-spc700.o: processor/spc700/spc700.cpp $(call rwildcard,processor/spc700)
|
||||
obj/processor-upd96050.o: processor/upd96050/upd96050.cpp $(call rwildcard,processor/upd96050)
|
||||
obj/processor-v30mz.o: processor/v30mz/v30mz.cpp $(call rwildcard,processor/v30mz)
|
||||
obj/processor-z80.o: processor/z80/z80.cpp $(call rwildcard,processor/z80)
|
||||
|
364
higan/processor/m68k/disassembler.cpp
Normal file
364
higan/processor/m68k/disassembler.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
template<> auto M68K::_read<Byte>(uint32 addr) -> uint32 {
|
||||
return read(0, addr);
|
||||
}
|
||||
|
||||
template<> auto M68K::_read<Word>(uint32 addr) -> uint32 {
|
||||
return read(1, addr);
|
||||
}
|
||||
|
||||
template<> auto M68K::_read<Long>(uint32 addr) -> uint32 {
|
||||
uint32 data = _read<Word>(addr + 0) << 16;
|
||||
return data | _read<Word>(addr + 2) << 0;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::_readPC() -> uint32 {
|
||||
auto data = _read<Size == Byte ? Word : Size>(_pc);
|
||||
_pc += Size == Long ? 4 : 2;
|
||||
return clip<Size>(data);
|
||||
}
|
||||
|
||||
auto M68K::_dataRegister(DataRegister dr) -> string {
|
||||
return {"d", dr.number};
|
||||
}
|
||||
|
||||
auto M68K::_addressRegister(AddressRegister ar) -> string {
|
||||
return {"a", ar.number};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::_immediate() -> string {
|
||||
return {"#$", hex(_readPC<Size>(), 2 << Size)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::_address(EffectiveAddress& ea) -> string {
|
||||
if(ea.mode == 9) return {"$", hex(_pc + (int16)_readPC(), 6L)};
|
||||
return "???";
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::_effectiveAddress(EffectiveAddress& ea) -> string {
|
||||
if(ea.mode == 0) return {_dataRegister(DataRegister{ea.reg})};
|
||||
if(ea.mode == 1) return {_addressRegister(AddressRegister{ea.reg})};
|
||||
if(ea.mode == 2) return {"(", _addressRegister(AddressRegister{ea.reg}), ")"};
|
||||
if(ea.mode == 3) return {"(", _addressRegister(AddressRegister{ea.reg}), ")+"};
|
||||
if(ea.mode == 4) return {"-(", _addressRegister(AddressRegister{ea.reg}), ")"};
|
||||
if(ea.mode == 5) return {"($", hex(read(AddressRegister{ea.reg}) + (int16)_readPC(), 6L), ")"};
|
||||
if(ea.mode == 7) return {"($", hex((int16)_readPC<Word>(), 6L), ")"};
|
||||
if(ea.mode == 8) return {"($", hex(_readPC<Long>(), 6L), ")"};
|
||||
if(ea.mode == 11) return {"#$", hex(_readPC<Size>(), 2 << Size)};
|
||||
return "???";
|
||||
}
|
||||
|
||||
auto M68K::_branch(uint8 displacement) -> string {
|
||||
uint16 extension = _readPC();
|
||||
_pc -= 2;
|
||||
int32 offset = displacement ? sign<Byte>(displacement) : sign<Word>(extension);
|
||||
return {"$", hex(_pc + offset, 6L)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::_suffix() -> string {
|
||||
return Size == Byte ? ".b" : Size == Word ? ".w" : ".l";
|
||||
}
|
||||
|
||||
auto M68K::_condition(uint4 condition) -> string {
|
||||
static const string conditions[16] = {
|
||||
"t ", "f ", "hi", "ls", "cc", "cs", "ne", "eq",
|
||||
"vc", "vs", "pl", "mi", "ge", "lt", "gt", "le",
|
||||
};
|
||||
return conditions[condition];
|
||||
}
|
||||
|
||||
auto M68K::disassemble(uint32 pc) -> string {
|
||||
uint16 opcode;
|
||||
return {hex(_pc = pc, 6L), " ", hex(opcode = _readPC(), 4L), " ", disassembleTable[opcode]()};
|
||||
}
|
||||
|
||||
auto M68K::disassembleRegisters() -> string {
|
||||
return {
|
||||
hex(r.d[0], 8L), " ", hex(r.d[1], 8L), " ", hex(r.d[2], 8L), " ", hex(r.d[3], 8L), " ",
|
||||
hex(r.d[4], 8L), " ", hex(r.d[5], 8L), " ", hex(r.d[6], 8L), " ", hex(r.d[7], 8L), " ",
|
||||
r.t ? "T" : "t",
|
||||
r.s ? "S" : "s",
|
||||
(uint)r.i,
|
||||
r.c ? "C" : "c",
|
||||
r.v ? "V" : "v",
|
||||
r.z ? "Z" : "z",
|
||||
r.n ? "N" : "n",
|
||||
r.x ? "X" : "x", "\n",
|
||||
hex(r.a[0], 8L), " ", hex(r.a[1], 8L), " ", hex(r.a[2], 8L), " ", hex(r.a[3], 8L), " ",
|
||||
hex(r.a[4], 8L), " ", hex(r.a[5], 8L), " ", hex(r.a[6], 8L), " ", hex(r.a[7], 8L), " ", hex(r.sp, 8L)
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<uint Size> auto M68K::disassembleADD(DataRegister dr, uint1 direction, EffectiveAddress ea) -> string {
|
||||
string op{"add", _suffix<Size>(), " "};
|
||||
|
||||
if(direction == 0) {
|
||||
return {op, _effectiveAddress<Size>(ea), ",", _dataRegister(dr)};
|
||||
} else {
|
||||
return {op, "", _dataRegister(dr), ",", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleADDA(AddressRegister ar, EffectiveAddress ea) -> string {
|
||||
return {"adda", _suffix<Size>(), " ", _effectiveAddress<Size>(ea), ",", _addressRegister(ar)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleADDI(EffectiveAddress ea) -> string {
|
||||
return {"addi", _suffix<Size>(), " ", _immediate<Size>(), ",", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleADDQ(uint4 immediate, EffectiveAddress modify) -> string {
|
||||
return {"addq", _suffix<Size>(), " #", immediate, ",", _effectiveAddress<Size>(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleADDX(EffectiveAddress target, EffectiveAddress source) -> string {
|
||||
return {"addx", _suffix<Size>(), " ", _effectiveAddress<Size>(target), ",", _effectiveAddress<Size>(source)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleANDI(EffectiveAddress ea) -> string {
|
||||
return {"andi", _suffix<Size>(), " ", _immediate<Size>(), ",", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleANDI_TO_CCR() -> string {
|
||||
return {"andi ", _immediate<Byte>(), ",ccr"};
|
||||
}
|
||||
|
||||
auto M68K::disassembleANDI_TO_SR() -> string {
|
||||
return {"andi ", _immediate<Word>(), ",sr"};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleASL(uint4 shift, DataRegister modify) -> string {
|
||||
return {"asl", _suffix<Size>(), " #", shift, ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleASL(DataRegister shift, DataRegister modify) -> string {
|
||||
return {"asl", _suffix<Size>(), " ", _dataRegister(shift), ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleASL(EffectiveAddress modify) -> string {
|
||||
return {"asl", _suffix<Word>(), " ", _effectiveAddress<Word>(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleASR(uint4 shift, DataRegister modify) -> string {
|
||||
return {"asr", _suffix<Size>(), " #", shift, ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleASR(DataRegister shift, DataRegister modify) -> string {
|
||||
return {"asr", _suffix<Size>(), " ", _dataRegister(shift), ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleASR(EffectiveAddress modify) -> string {
|
||||
return {"asr", _suffix<Word>(), " ", _effectiveAddress<Word>(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleBCC(uint4 condition, uint8 displacement) -> string {
|
||||
auto cc = _condition(condition);
|
||||
if(condition == 0) cc = "ra";
|
||||
if(condition == 1) cc = "sr";
|
||||
return {"b", cc, " ", _branch(displacement)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleBTST(DataRegister dr, EffectiveAddress ea) -> string {
|
||||
return {"btst ", _dataRegister(dr), ",", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleBTST(EffectiveAddress ea) -> string {
|
||||
return {"btst ", _immediate<Byte>(), ",", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleCLR(EffectiveAddress ea) -> string {
|
||||
return {"clr", _suffix<Size>(), " ", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleCMP(DataRegister dr, EffectiveAddress ea) -> string {
|
||||
return {"cmp", _suffix<Size>(), " ", _effectiveAddress<Size>(ea), ",", _dataRegister(dr)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleCMPA(AddressRegister ar, EffectiveAddress ea) -> string {
|
||||
return {"cmpa", _suffix<Size>(), " ", _effectiveAddress<Size>(ea), ",", _addressRegister(ar)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleCMPI(EffectiveAddress ea) -> string {
|
||||
return {"cmpi", _suffix<Size>(), " ", _immediate<Size>(), ",", _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleCMPM(EffectiveAddress ax, EffectiveAddress ay) -> string {
|
||||
return {"cmpm", _suffix<Size>(), " ", _effectiveAddress<Size>(ay), ",", _effectiveAddress<Size>(ax)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleDBCC(uint4 condition, DataRegister dr) -> string {
|
||||
auto base = _pc;
|
||||
auto displacement = (int16)_readPC();
|
||||
return {"db", _condition(condition), " ", _dataRegister(dr), ",$", hex(base + displacement, 6L)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleEORI_TO_CCR() -> string {
|
||||
return {"eori ", _immediate<Byte>(), ",ccr"};
|
||||
}
|
||||
|
||||
auto M68K::disassembleEORI_TO_SR() -> string {
|
||||
return {"eori ", _immediate<Word>(), ",sr"};
|
||||
}
|
||||
|
||||
auto M68K::disassembleJSR(EffectiveAddress target) -> string {
|
||||
return {"jsr ", _effectiveAddress<Long>(target)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleLEA(AddressRegister ar, EffectiveAddress ea) -> string {
|
||||
return {"lea ", _address<Long>(ea), ",", _addressRegister(ar)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleLSL(uint4 immediate, DataRegister dr) -> string {
|
||||
return {"lsl", _suffix<Size>(), " #", immediate, ",", _dataRegister(dr)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleLSL(DataRegister sr, DataRegister dr) -> string {
|
||||
return {"lsl", _suffix<Size>(), " ", _dataRegister(sr), ",", _dataRegister(dr)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleLSL(EffectiveAddress ea) -> string {
|
||||
return {"lsl", _suffix<Word>(), " ", _effectiveAddress<Word>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleLSR(uint4 immediate, DataRegister dr) -> string {
|
||||
return {"lsr", _suffix<Size>(), " #", immediate, ",", _dataRegister(dr)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleLSR(DataRegister shift, DataRegister dr) -> string {
|
||||
return {"lsr", _suffix<Size>(), " ", _dataRegister(shift), ",", _dataRegister(dr)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleLSR(EffectiveAddress ea) -> string {
|
||||
return {"lsr", _suffix<Word>(), " ", _effectiveAddress<Word>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleMOVE(EffectiveAddress to, EffectiveAddress from) -> string {
|
||||
return {"move", _suffix<Size>(), " ", _effectiveAddress<Size>(from), ",", _effectiveAddress<Size>(to)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleMOVEA(AddressRegister ar, EffectiveAddress ea) -> string {
|
||||
return {"movea ", _effectiveAddress<Size>(ea), ",", _addressRegister(ar)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleMOVEM(uint1 direction, EffectiveAddress ea) -> string {
|
||||
string op{"movem", _suffix<Size>(), " "};
|
||||
|
||||
uint16 list = _readPC();
|
||||
string regs;
|
||||
for(uint n : range(8)) if(list.bit(0 + n)) regs.append(_dataRegister(DataRegister{n}), ",");
|
||||
regs.trimRight(",");
|
||||
if(regs && list >> 8) regs.append("/");
|
||||
for(uint n : range(8)) if(list.bit(8 + n)) regs.append(_addressRegister(AddressRegister{n}), ",");
|
||||
regs.trimRight(",");
|
||||
|
||||
if(direction == 0) {
|
||||
return {op, regs, ",", _effectiveAddress<Size>(ea)};
|
||||
} else {
|
||||
return {op, _effectiveAddress<Size>(ea), ",", regs};
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEQ(DataRegister dr, uint8 immediate) -> string {
|
||||
return {"moveq #$", hex(immediate, 2L), ",", _dataRegister(dr)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE_FROM_SR(EffectiveAddress ea) -> string {
|
||||
return {"move sr,", _effectiveAddress<Word>(ea)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE_TO_CCR(EffectiveAddress ea) -> string {
|
||||
return {"move ", _effectiveAddress<Byte>(ea), ",ccr"};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE_TO_SR(EffectiveAddress ea) -> string {
|
||||
return {"move ", _effectiveAddress<Word>(ea), ",sr"};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE_USP(uint1 direction, AddressRegister ar) -> string {
|
||||
if(direction == 0) {
|
||||
return {"move ", _addressRegister(ar), ",usp"};
|
||||
} else {
|
||||
return {"move usp,", _addressRegister(ar)};
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::disassembleNOP() -> string {
|
||||
return {"nop "};
|
||||
}
|
||||
|
||||
auto M68K::disassembleORI_TO_CCR() -> string {
|
||||
return {"ori ", _immediate<Byte>(), ",ccr"};
|
||||
}
|
||||
|
||||
auto M68K::disassembleORI_TO_SR() -> string {
|
||||
return {"ori ", _immediate<Word>(), ",sr"};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROL(uint4 shift, DataRegister modify) -> string {
|
||||
return {"rol", _suffix<Size>(), " #", shift, ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROL(DataRegister shift, DataRegister modify) -> string {
|
||||
return {"rol", _suffix<Size>(), " ", _dataRegister(shift), ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleROL(EffectiveAddress modify) -> string {
|
||||
return {"rol", _suffix<Word>(), " ", _effectiveAddress<Word>(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROR(uint4 shift, DataRegister modify) -> string {
|
||||
return {"ror", _suffix<Size>(), " #", shift, ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROR(DataRegister shift, DataRegister modify) -> string {
|
||||
return {"ror", _suffix<Size>(), " ", _dataRegister(shift) ,",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleROR(EffectiveAddress modify) -> string {
|
||||
return {"ror", _suffix<Word>(), " ", _effectiveAddress<Word>(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROXL(uint4 shift, DataRegister modify) -> string {
|
||||
return {"roxl", _suffix<Size>(), " #", shift, ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROXL(DataRegister shift, DataRegister modify) -> string {
|
||||
return {"roxl", _suffix<Size>(), " ", _dataRegister(shift), ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleROXL(EffectiveAddress modify) -> string {
|
||||
return {"roxl", _suffix<Word>(), " ", _effectiveAddress<Word>(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROXR(uint4 shift, DataRegister modify) -> string {
|
||||
return {"roxr", _suffix<Size>(), " #", shift, ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleROXR(DataRegister shift, DataRegister modify) -> string {
|
||||
return {"roxr", _suffix<Size>(), " ", _dataRegister(shift), ",", _dataRegister(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleROXR(EffectiveAddress modify) -> string {
|
||||
return {"roxr", _suffix<Word>(), " ", _effectiveAddress<Word>(modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleRTS() -> string {
|
||||
return {"rts "};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleSUB(EffectiveAddress source, DataRegister target) -> string {
|
||||
return {"sub", _suffix<Size>(), " ", _effectiveAddress<Size>(source), ",", _dataRegister(target)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleSUB(DataRegister source, EffectiveAddress target) -> string {
|
||||
return {"sub", _suffix<Size>(), " ", _dataRegister(source), ",", _effectiveAddress<Size>(target)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleSUBQ(uint4 immediate, EffectiveAddress ea) -> string {
|
||||
return {"subq", _suffix<Size>(), " #", immediate, _effectiveAddress<Size>(ea)};
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::disassembleTST(EffectiveAddress ea) -> string {
|
||||
return {"tst", _suffix<Size>(), " ", _effectiveAddress<Size>(ea)};
|
||||
}
|
207
higan/processor/m68k/effective-address.cpp
Normal file
207
higan/processor/m68k/effective-address.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
template<uint Size> auto M68K::fetch(EffectiveAddress& ea) -> uint32 {
|
||||
if(!ea.valid.raise()) return ea.address;
|
||||
|
||||
switch(ea.mode) {
|
||||
|
||||
case DataRegisterDirect: {
|
||||
return read(DataRegister{ea.reg});
|
||||
}
|
||||
|
||||
case AddressRegisterDirect: {
|
||||
return read(AddressRegister{ea.reg});
|
||||
}
|
||||
|
||||
case AddressRegisterIndirect: {
|
||||
return read(AddressRegister{ea.reg});
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPostIncrement: {
|
||||
return read(AddressRegister{ea.reg});
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPreDecrement: {
|
||||
return read(AddressRegister{ea.reg});
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithDisplacement: {
|
||||
return read(AddressRegister{ea.reg}) + (int16)readPC();
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithIndex: {
|
||||
auto extension = readPC();
|
||||
auto index = extension & 0x8000
|
||||
? read(AddressRegister{extension >> 12})
|
||||
: read(DataRegister{extension >> 12});
|
||||
if(extension & 0x800) index = (int16)index;
|
||||
return read(AddressRegister{ea.reg}) + index + (int8)extension;
|
||||
}
|
||||
|
||||
case AbsoluteShortIndirect: {
|
||||
return (int16)readPC();
|
||||
}
|
||||
|
||||
case AbsoluteLongIndirect: {
|
||||
return readPC<Long>();
|
||||
}
|
||||
|
||||
case ProgramCounterIndirectWithDisplacement: {
|
||||
auto base = r.pc;
|
||||
return base + (int16)readPC();
|
||||
}
|
||||
|
||||
case ProgramCounterIndirectWithIndex: {
|
||||
auto base = r.pc;
|
||||
auto extension = readPC();
|
||||
auto index = extension & 0x8000
|
||||
? read(AddressRegister{extension >> 12})
|
||||
: read(DataRegister{extension >> 12});
|
||||
if(extension & 0x800) index = (int16)index;
|
||||
return base + index + (int8)extension;
|
||||
}
|
||||
|
||||
case Immediate: {
|
||||
return readPC<Size>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<uint Size, bool Update> auto M68K::read(EffectiveAddress& ea) -> uint32 {
|
||||
ea.address = fetch<Size>(ea);
|
||||
|
||||
switch(ea.mode) {
|
||||
|
||||
case DataRegisterDirect: {
|
||||
return clip<Size>(ea.address);
|
||||
}
|
||||
|
||||
case AddressRegisterDirect: {
|
||||
return clip<Size>(ea.address);
|
||||
}
|
||||
|
||||
case AddressRegisterIndirect: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPostIncrement: {
|
||||
auto data = read<Size>(ea.address);
|
||||
if(Update) write(AddressRegister{ea.reg}, ea.address += bytes<Size>());
|
||||
return data;
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPreDecrement: {
|
||||
auto data = read<Size>(ea.address - bytes<Size>());
|
||||
if(Update) write(AddressRegister{ea.reg}, ea.address -= bytes<Size>());
|
||||
return data;
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithDisplacement: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithIndex: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case AbsoluteShortIndirect: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case AbsoluteLongIndirect: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case ProgramCounterIndirectWithDisplacement: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case ProgramCounterIndirectWithIndex: {
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
case Immediate: {
|
||||
return clip<Size>(ea.address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<uint Size, bool Update> auto M68K::write(EffectiveAddress& ea, uint32 data) -> void {
|
||||
ea.address = fetch<Size>(ea);
|
||||
|
||||
switch(ea.mode) {
|
||||
|
||||
case DataRegisterDirect: {
|
||||
return write<Size>(DataRegister{ea.reg}, data);
|
||||
}
|
||||
|
||||
case AddressRegisterDirect: {
|
||||
return write<Size>(AddressRegister{ea.reg}, data);
|
||||
}
|
||||
|
||||
case AddressRegisterIndirect: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPostIncrement: {
|
||||
write<Size>(ea.address, data);
|
||||
if(Update) write(AddressRegister{ea.reg}, ea.address += bytes<Size>());
|
||||
return;
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPreDecrement: {
|
||||
write<Size, Reverse>(ea.address - bytes<Size>(), data);
|
||||
if(Update) write(AddressRegister{ea.reg}, ea.address -= bytes<Size>());
|
||||
return;
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithDisplacement: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithIndex: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case AbsoluteShortIndirect: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case AbsoluteLongIndirect: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case ProgramCounterIndirectWithDisplacement: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case ProgramCounterIndirectWithIndex: {
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case Immediate: {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::flush(EffectiveAddress& ea, uint32 data) -> void {
|
||||
switch(ea.mode) {
|
||||
|
||||
case AddressRegisterIndirectWithPostIncrement: {
|
||||
write<Size>(AddressRegister{ea.reg}, data);
|
||||
return;
|
||||
}
|
||||
|
||||
case AddressRegisterIndirectWithPreDecrement: {
|
||||
write<Size>(AddressRegister{ea.reg}, data);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
738
higan/processor/m68k/instruction.cpp
Normal file
738
higan/processor/m68k/instruction.cpp
Normal file
@@ -0,0 +1,738 @@
|
||||
auto M68K::trap() -> void {
|
||||
instructionsExecuted--;
|
||||
r.pc -= 2;
|
||||
print("[M68K] unimplemented instruction: ", hex(r.pc, 6L), " = ", hex(opcode, 4L), "\n");
|
||||
print("[M68K] instructions executed: ", instructionsExecuted, "\n");
|
||||
while(true) step(5);
|
||||
}
|
||||
|
||||
auto M68K::instruction() -> void {
|
||||
instructionsExecuted++;
|
||||
|
||||
//if(instructionsExecuted >= 2000010) trap();
|
||||
|
||||
//if(instructionsExecuted >= 2000000) {
|
||||
// print(disassembleRegisters(), "\n");
|
||||
// print(disassemble(r.pc), "\n");
|
||||
// print("\n");
|
||||
//}
|
||||
|
||||
opcode = readPC();
|
||||
return instructionTable[opcode]();
|
||||
}
|
||||
|
||||
M68K::M68K() {
|
||||
#define bind(id, name, ...) { \
|
||||
assert(!instructionTable[id]); \
|
||||
instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \
|
||||
disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); }; \
|
||||
}
|
||||
|
||||
#define unbind(id) { \
|
||||
instructionTable[id].reset(); \
|
||||
disassembleTable[id].reset(); \
|
||||
}
|
||||
|
||||
#define pattern(s) \
|
||||
std::integral_constant<uint16_t, bit::test(s)>::value
|
||||
|
||||
//ADD
|
||||
for(uint3 dreg : range(8))
|
||||
for(uint1 direction : range(2))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1101 ---- ++-- ----") | dreg << 9 | direction << 8 | mode << 3 | reg << 0;
|
||||
if(direction == 1 && (mode == 0 || mode == 1 || (mode == 7 && reg >= 2))) continue;
|
||||
|
||||
DataRegister dr{dreg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, ADD<Byte>, dr, direction, ea);
|
||||
bind(opcode | 1 << 6, ADD<Word>, dr, direction, ea);
|
||||
bind(opcode | 2 << 6, ADD<Long>, dr, direction, ea);
|
||||
|
||||
if(direction == 0 && mode == 1) unbind(opcode | 0 << 6);
|
||||
}
|
||||
|
||||
//ADDA
|
||||
for(uint3 areg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1101 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 5) continue;
|
||||
|
||||
AddressRegister ar{areg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 8, ADDA<Word>, ar, ea);
|
||||
bind(opcode | 1 << 8, ADDA<Long>, ar, ea);
|
||||
}
|
||||
|
||||
//ADDI
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0000 0110 ++-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode | 0 << 6, ADDI<Byte>, modify);
|
||||
bind(opcode | 1 << 6, ADDI<Word>, modify);
|
||||
bind(opcode | 2 << 6, ADDI<Long>, modify);
|
||||
}
|
||||
|
||||
//ADDQ
|
||||
for(uint3 data : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0101 ---0 ++-- ----") | data << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 2) continue;
|
||||
|
||||
uint4 immediate = data ? (uint4)data : (uint4)8;
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode | 0 << 6, ADDQ<Byte>, immediate, modify);
|
||||
bind(opcode | 1 << 6, ADDQ<Word>, immediate, modify);
|
||||
bind(opcode | 2 << 6, ADDQ<Long>, immediate, modify);
|
||||
|
||||
if(mode == 1) unbind(opcode | 0 << 6);
|
||||
}
|
||||
|
||||
//ADDX
|
||||
for(uint3 treg : range(8))
|
||||
for(uint3 sreg : range(8)) {
|
||||
auto opcode = pattern("1101 ---1 ++00 ----") | treg << 9 | sreg << 0;
|
||||
|
||||
EffectiveAddress dataTarget{DataRegisterDirect, treg};
|
||||
EffectiveAddress dataSource{DataRegisterDirect, sreg};
|
||||
bind(opcode | 0 << 6 | 0 << 3, ADDX<Byte>, dataTarget, dataSource);
|
||||
bind(opcode | 1 << 6 | 0 << 3, ADDX<Word>, dataTarget, dataSource);
|
||||
bind(opcode | 2 << 6 | 0 << 3, ADDX<Long>, dataTarget, dataSource);
|
||||
|
||||
EffectiveAddress addressTarget{AddressRegisterIndirectWithPreDecrement, treg};
|
||||
EffectiveAddress addressSource{AddressRegisterIndirectWithPreDecrement, sreg};
|
||||
bind(opcode | 0 << 6 | 1 << 3, ADDX<Byte>, addressTarget, addressSource);
|
||||
bind(opcode | 1 << 6 | 1 << 3, ADDX<Word>, addressTarget, addressSource);
|
||||
bind(opcode | 2 << 6 | 1 << 3, ADDX<Long>, addressTarget, addressSource);
|
||||
}
|
||||
|
||||
//ANDI
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0000 0010 ++-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, ANDI<Byte>, ea);
|
||||
bind(opcode | 1 << 6, ANDI<Word>, ea);
|
||||
bind(opcode | 2 << 6, ANDI<Long>, ea);
|
||||
}
|
||||
|
||||
//ANDI_TO_CCR
|
||||
{ auto opcode = pattern("0000 0010 0011 1100");
|
||||
|
||||
bind(opcode, ANDI_TO_CCR);
|
||||
}
|
||||
|
||||
//ANDI_TO_SR
|
||||
{ auto opcode = pattern("0000 0010 0111 1100");
|
||||
|
||||
bind(opcode, ANDI_TO_SR);
|
||||
}
|
||||
|
||||
//ASL (immediate)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++00 0---") | count << 9 | dreg << 0;
|
||||
|
||||
auto shift = count ? (uint4)count : (uint4)8;
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ASL<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ASL<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ASL<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ASL (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++10 0---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{sreg};
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ASL<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ASL<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ASL<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ASL (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0001 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode, ASL, modify);
|
||||
}
|
||||
|
||||
//ASR (immediate)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++00 0---") | count << 9 | dreg << 0;
|
||||
|
||||
auto shift = count ? (uint4)count : (uint4)8;
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ASR<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ASR<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ASR<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ASR (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++10 0---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{sreg};
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ASR<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ASR<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ASR<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ASR (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0000 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode, ASR, modify);
|
||||
}
|
||||
|
||||
//BCC
|
||||
for(uint4 condition : range( 16))
|
||||
for(uint8 displacement : range(256)) {
|
||||
auto opcode = pattern("0110 ---- ---- ----") | condition << 8 | displacement << 0;
|
||||
|
||||
bind(opcode, BCC, condition, displacement);
|
||||
}
|
||||
|
||||
//BTST (register)
|
||||
for(uint3 dreg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0000 ---1 00-- ----") | dreg << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 5)) continue;
|
||||
|
||||
DataRegister dr{dreg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
if(mode == 0) bind(opcode, BTST<Long>, dr, ea);
|
||||
if(mode != 0) bind(opcode, BTST<Byte>, dr, ea);
|
||||
}
|
||||
|
||||
//BTST (immediate)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0000 1000 00-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && (reg == 2 || reg >= 5))) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
if(mode == 0) bind(opcode, BTST<Long>, ea);
|
||||
if(mode != 0) bind(opcode, BTST<Byte>, ea);
|
||||
}
|
||||
|
||||
//CLR
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 0010 ++-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, CLR<Byte>, ea);
|
||||
bind(opcode | 1 << 6, CLR<Word>, ea);
|
||||
bind(opcode | 2 << 6, CLR<Long>, ea);
|
||||
}
|
||||
|
||||
//CMP
|
||||
for(uint3 dreg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1011 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 5) continue;
|
||||
|
||||
DataRegister dr{dreg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, CMP<Byte>, dr, ea);
|
||||
bind(opcode | 1 << 6, CMP<Word>, dr, ea);
|
||||
bind(opcode | 2 << 6, CMP<Long>, dr, ea);
|
||||
|
||||
if(mode == 1) unbind(opcode | 0 << 6);
|
||||
}
|
||||
|
||||
//CMPA
|
||||
for(uint3 areg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1011 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||
|
||||
AddressRegister ar{areg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 8, CMPA<Word>, ar, ea);
|
||||
bind(opcode | 1 << 8, CMPA<Long>, ar, ea);
|
||||
}
|
||||
|
||||
//CMPI
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0000 1100 ++-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, CMPI<Byte>, ea);
|
||||
bind(opcode | 1 << 6, CMPI<Word>, ea);
|
||||
bind(opcode | 2 << 6, CMPI<Long>, ea);
|
||||
}
|
||||
|
||||
//CMPM
|
||||
for(uint3 xreg : range(8))
|
||||
for(uint3 yreg : range(8)) {
|
||||
auto opcode = pattern("1011 ---1 ++00 1---") | xreg << 9 | yreg << 0;
|
||||
|
||||
EffectiveAddress ax{AddressRegisterIndirectWithPostIncrement, xreg};
|
||||
EffectiveAddress ay{AddressRegisterIndirectWithPostIncrement, yreg};
|
||||
bind(opcode | 0 << 6, CMPM<Byte>, ax, ay);
|
||||
bind(opcode | 1 << 6, CMPM<Word>, ax, ay);
|
||||
bind(opcode | 2 << 6, CMPM<Long>, ax, ay);
|
||||
}
|
||||
|
||||
//DBCC
|
||||
for(uint4 condition : range(16))
|
||||
for(uint3 dreg : range( 8)) {
|
||||
auto opcode = pattern("0101 ---- 1100 1---") | condition << 8 | dreg << 0;
|
||||
|
||||
DataRegister dr{dreg};
|
||||
bind(opcode, DBCC, condition, dr);
|
||||
}
|
||||
|
||||
//EORI_TO_CCR
|
||||
{ auto opcode = pattern("0000 1010 0011 1100");
|
||||
|
||||
bind(opcode, EORI_TO_CCR);
|
||||
}
|
||||
|
||||
//EORI_TO_SR
|
||||
{ auto opcode = pattern("0000 1010 0111 1100");
|
||||
|
||||
bind(opcode, EORI_TO_SR);
|
||||
}
|
||||
|
||||
//JSR
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 1110 10-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue;
|
||||
|
||||
EffectiveAddress target{mode, reg};
|
||||
bind(opcode, JSR, target);
|
||||
}
|
||||
|
||||
//LEA
|
||||
for(uint3 areg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 ---1 11-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || mode == 3 || mode == 4 || (mode == 7 && reg >= 4)) continue;
|
||||
|
||||
AddressRegister ar{areg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode, LEA, ar, ea);
|
||||
}
|
||||
|
||||
//LSL (immediate)
|
||||
for(uint3 data : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++00 1---") | data << 9 | dreg << 0;
|
||||
|
||||
auto immediate = data ? (uint4)data : (uint4)8;
|
||||
DataRegister dr{dreg};
|
||||
bind(opcode | 0 << 6, LSL<Byte>, immediate, dr);
|
||||
bind(opcode | 1 << 6, LSL<Word>, immediate, dr);
|
||||
bind(opcode | 2 << 6, LSL<Long>, immediate, dr);
|
||||
}
|
||||
|
||||
//LSL (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++10 1---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister sr{sreg};
|
||||
DataRegister dr{dreg};
|
||||
bind(opcode | 0 << 6, LSL<Byte>, sr, dr);
|
||||
bind(opcode | 1 << 6, LSL<Word>, sr, dr);
|
||||
bind(opcode | 2 << 6, LSL<Long>, sr, dr);
|
||||
}
|
||||
|
||||
//LSL (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0011 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode, LSL, ea);
|
||||
}
|
||||
|
||||
//LSR (immediate)
|
||||
for(uint3 data : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++00 1---") | data << 9 | dreg << 0;
|
||||
|
||||
auto immediate = data ? (uint4)data : (uint4)8;
|
||||
DataRegister dr{dreg};
|
||||
bind(opcode | 0 << 6, LSR<Byte>, immediate, dr);
|
||||
bind(opcode | 1 << 6, LSR<Word>, immediate, dr);
|
||||
bind(opcode | 2 << 6, LSR<Long>, immediate, dr);
|
||||
}
|
||||
|
||||
//LSR (register)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++10 1---") | count << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{count};
|
||||
DataRegister dr{dreg};
|
||||
bind(opcode | 0 << 6, LSR<Byte>, shift, dr);
|
||||
bind(opcode | 1 << 6, LSR<Word>, shift, dr);
|
||||
bind(opcode | 2 << 6, LSR<Long>, shift, dr);
|
||||
}
|
||||
|
||||
//LSR (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0010 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode, LSR, ea);
|
||||
}
|
||||
|
||||
//MOVE
|
||||
for(uint3 toReg : range(8))
|
||||
for(uint3 toMode : range(8))
|
||||
for(uint3 fromMode : range(8))
|
||||
for(uint3 fromReg : range(8)) {
|
||||
auto opcode = pattern("00++ ---- ---- ----") | toReg << 9 | toMode << 6 | fromMode << 3 | fromReg << 0;
|
||||
if(toMode == 1 || (toMode == 7 && toReg >= 2)) continue;
|
||||
if(fromMode == 7 && fromReg >= 5) continue;
|
||||
|
||||
EffectiveAddress to{toMode, toReg};
|
||||
EffectiveAddress from{fromMode, fromReg};
|
||||
bind(opcode | 1 << 12, MOVE<Byte>, to, from);
|
||||
bind(opcode | 3 << 12, MOVE<Word>, to, from);
|
||||
bind(opcode | 2 << 12, MOVE<Long>, to, from);
|
||||
|
||||
if(fromMode == 1) unbind(opcode | 1 << 12);
|
||||
}
|
||||
|
||||
//MOVEA
|
||||
for(uint3 areg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("00++ ---0 01-- ----") | areg << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 5) continue;
|
||||
|
||||
AddressRegister ar{areg};
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 3 << 12, MOVEA<Word>, ar, ea);
|
||||
bind(opcode | 2 << 12, MOVEA<Long>, ar, ea);
|
||||
}
|
||||
|
||||
//MOVEM
|
||||
for(uint1 direction : range(2))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 1-00 1+-- ----") | direction << 10 | mode << 3 | reg << 0;
|
||||
if(direction == 0 && (mode <= 1 || mode == 3 || (mode == 7 && reg >= 2)));
|
||||
if(direction == 1 && (mode <= 1 || mode == 4 || (mode == 7 && reg >= 4)));
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, MOVEM<Word>, direction, ea);
|
||||
bind(opcode | 1 << 6, MOVEM<Long>, direction, ea);
|
||||
}
|
||||
|
||||
//MOVEQ
|
||||
for(uint3 dreg : range( 8))
|
||||
for(uint8 immediate : range(256)) {
|
||||
auto opcode = pattern("0111 ---0 ---- ----") | dreg << 9 | immediate << 0;
|
||||
|
||||
DataRegister dr{dreg};
|
||||
bind(opcode, MOVEQ, dr, immediate);
|
||||
}
|
||||
|
||||
//MOVE_FROM_SR
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 0000 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode, MOVE_FROM_SR, ea);
|
||||
}
|
||||
|
||||
//MOVE_TO_CCR
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 0100 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 5)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode, MOVE_TO_CCR, ea);
|
||||
}
|
||||
|
||||
//MOVE_TO_SR
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 0110 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 1 || (mode == 7 && reg >= 5)) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode, MOVE_TO_SR, ea);
|
||||
}
|
||||
|
||||
//MOVE_USP
|
||||
for(uint1 direction : range(2))
|
||||
for(uint3 areg : range(8)) {
|
||||
auto opcode = pattern("0100 1110 0110 ----") | direction << 3 | areg << 0;
|
||||
|
||||
AddressRegister ar{areg};
|
||||
bind(opcode, MOVE_USP, direction, ar);
|
||||
}
|
||||
|
||||
//NOP
|
||||
{ auto opcode = pattern("0100 1110 0111 0001");
|
||||
|
||||
bind(opcode, NOP);
|
||||
}
|
||||
|
||||
//ORI_TO_CCR
|
||||
{ auto opcode = pattern("0000 0000 0011 1100");
|
||||
|
||||
bind(opcode, ORI_TO_CCR);
|
||||
}
|
||||
|
||||
//ORI_TO_SR
|
||||
{ auto opcode = pattern("0000 0000 0111 1100");
|
||||
|
||||
bind(opcode, ORI_TO_SR);
|
||||
}
|
||||
|
||||
//ROL (immediate)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++01 1---") | count << 9 | dreg << 0;
|
||||
|
||||
auto shift = count ? (uint4)count : (uint4)8;
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROL<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROL<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROL<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROL (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++11 1---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{sreg};
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROL<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROL<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROL<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROL (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0111 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode, ROL, modify);
|
||||
}
|
||||
|
||||
//ROR (immediate)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++01 1---") | count << 9 | dreg << 0;
|
||||
|
||||
auto shift = count ? (uint4)count : (uint4)8;
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROR<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROR<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROR<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROR (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++11 1---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{sreg};
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROR<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROR<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROR<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROR (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0110 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode, ROR, modify);
|
||||
}
|
||||
|
||||
//ROXL (immediate)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++01 0---") | count << 9 | dreg << 0;
|
||||
|
||||
auto shift = count ? (uint4)count : (uint4)8;
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROXL<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROXL<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROXL<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROXL (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---1 ++11 0---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{sreg};
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROXL<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROXL<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROXL<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROXL (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0101 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode, ROXL, modify);
|
||||
}
|
||||
|
||||
//ROXR (immediate)
|
||||
for(uint3 count : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++01 0---") | count << 9 | dreg << 0;
|
||||
|
||||
auto shift = count ? (uint4)count : (uint4)8;
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROXR<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROXR<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROXR<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROXR (register)
|
||||
for(uint3 sreg : range(8))
|
||||
for(uint3 dreg : range(8)) {
|
||||
auto opcode = pattern("1110 ---0 ++11 0---") | sreg << 9 | dreg << 0;
|
||||
|
||||
DataRegister shift{sreg};
|
||||
DataRegister modify{dreg};
|
||||
bind(opcode | 0 << 6, ROXR<Byte>, shift, modify);
|
||||
bind(opcode | 1 << 6, ROXR<Word>, shift, modify);
|
||||
bind(opcode | 2 << 6, ROXR<Long>, shift, modify);
|
||||
}
|
||||
|
||||
//ROXR (effective address)
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1110 0100 11-- ----") | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
EffectiveAddress modify{mode, reg};
|
||||
bind(opcode, ROXR, modify);
|
||||
}
|
||||
|
||||
//RTS
|
||||
{ auto opcode = pattern("0100 1110 0111 0101");
|
||||
|
||||
bind(opcode, RTS);
|
||||
}
|
||||
|
||||
//SUB
|
||||
for(uint3 dreg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1001 ---0 ++-- ----") | dreg << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 5) continue;
|
||||
|
||||
EffectiveAddress source{mode, reg};
|
||||
DataRegister target{dreg};
|
||||
bind(opcode | 0 << 6, SUB<Byte>, source, target);
|
||||
bind(opcode | 1 << 6, SUB<Word>, source, target);
|
||||
bind(opcode | 2 << 6, SUB<Long>, source, target);
|
||||
|
||||
if(mode == 1) unbind(opcode | 0 << 6);
|
||||
}
|
||||
|
||||
//SUB
|
||||
for(uint3 dreg : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("1001 ---1 ++-- ----") | dreg << 9 | mode << 3 | reg << 0;
|
||||
if(mode <= 1 || (mode == 7 && reg >= 2)) continue;
|
||||
|
||||
DataRegister source{dreg};
|
||||
EffectiveAddress target{mode, reg};
|
||||
bind(opcode | 0 << 6, SUB<Byte>, source, target);
|
||||
bind(opcode | 1 << 6, SUB<Word>, source, target);
|
||||
bind(opcode | 2 << 6, SUB<Long>, source, target);
|
||||
}
|
||||
|
||||
//SUBQ
|
||||
for(uint3 data : range(8))
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0101 ---1 ++-- ----") | data << 9 | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 2) continue;
|
||||
|
||||
auto immediate = data ? (uint4)data : (uint4)8;
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, SUBQ<Byte>, immediate, ea);
|
||||
bind(opcode | 1 << 6, SUBQ<Word>, immediate, ea);
|
||||
bind(opcode | 2 << 6, SUBQ<Long>, immediate, ea);
|
||||
|
||||
if(mode == 1) unbind(opcode | 0 << 6);
|
||||
}
|
||||
|
||||
//TST
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0;
|
||||
if(mode == 7 && reg >= 2) continue;
|
||||
|
||||
EffectiveAddress ea{mode, reg};
|
||||
bind(opcode | 0 << 6, TST<Byte>, ea);
|
||||
bind(opcode | 1 << 6, TST<Word>, ea);
|
||||
bind(opcode | 2 << 6, TST<Long>, ea);
|
||||
|
||||
if(mode == 1) unbind(opcode | 0 << 6);
|
||||
}
|
||||
|
||||
#undef bind
|
||||
#undef unbind
|
||||
#undef pattern
|
||||
|
||||
uint unimplemented = 0;
|
||||
for(uint16 opcode : range(65536)) {
|
||||
if(instructionTable[opcode]) continue;
|
||||
instructionTable[opcode] = [=] { trap(); };
|
||||
disassembleTable[opcode] = [=] { return string{"???"}; };
|
||||
unimplemented++;
|
||||
}
|
||||
print("[M68K] unimplemented opcodes: ", unimplemented, "\n");
|
||||
}
|
645
higan/processor/m68k/instructions.cpp
Normal file
645
higan/processor/m68k/instructions.cpp
Normal file
@@ -0,0 +1,645 @@
|
||||
auto M68K::testCondition(uint4 condition) -> bool {
|
||||
switch(condition) {
|
||||
case 0: return true; //T
|
||||
case 1: return false; //F
|
||||
case 2: return !r.c && !r.z; //HI
|
||||
case 3: return r.c || r.z; //LS
|
||||
case 4: return !r.c; //CC,HS
|
||||
case 5: return r.c; //CS,LO
|
||||
case 6: return !r.z; //NE
|
||||
case 7: return r.z; //EQ
|
||||
case 8: return !r.v; //VC
|
||||
case 9: return r.v; //VS
|
||||
case 10: return !r.n; //PL
|
||||
case 11: return r.n; //MI
|
||||
case 12: return r.n == r.v; //GE
|
||||
case 13: return r.n != r.v; //LT
|
||||
case 14: return r.n == r.v && !r.z; //GT
|
||||
case 15: return r.n != r.v || r.z; //LE
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<> auto M68K::bytes<Byte>() -> uint { return 1; }
|
||||
template<> auto M68K::bytes<Word>() -> uint { return 2; }
|
||||
template<> auto M68K::bytes<Long>() -> uint { return 4; }
|
||||
|
||||
template<> auto M68K::bits<Byte>() -> uint { return 8; }
|
||||
template<> auto M68K::bits<Word>() -> uint { return 16; }
|
||||
template<> auto M68K::bits<Long>() -> uint { return 32; }
|
||||
|
||||
template<uint Size> auto M68K::lsb() -> uint32 { return 1; }
|
||||
|
||||
template<> auto M68K::msb<Byte>() -> uint32 { return 0x80; }
|
||||
template<> auto M68K::msb<Word>() -> uint32 { return 0x8000; }
|
||||
template<> auto M68K::msb<Long>() -> uint32 { return 0x80000000; }
|
||||
|
||||
template<> auto M68K::mask<Byte>() -> uint32 { return 0xff; }
|
||||
template<> auto M68K::mask<Word>() -> uint32 { return 0xffff; }
|
||||
template<> auto M68K::mask<Long>() -> uint32 { return 0xffffffff; }
|
||||
|
||||
template<> auto M68K::clip<Byte>(uint32 data) -> uint32 { return data & 0xff; }
|
||||
template<> auto M68K::clip<Word>(uint32 data) -> uint32 { return data & 0xffff; }
|
||||
template<> auto M68K::clip<Long>(uint32 data) -> uint32 { return data & 0xffffffff; }
|
||||
|
||||
template<> auto M68K::sign<Byte>(uint32 data) -> int32 { return (int8)data; }
|
||||
template<> auto M68K::sign<Word>(uint32 data) -> int32 { return (int16)data; }
|
||||
template<> auto M68K::sign<Long>(uint32 data) -> int32 { return (int32)data; }
|
||||
|
||||
template<uint Size> auto M68K::zero(uint32 result) -> bool {
|
||||
return clip<Size>(result) == 0;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::negative(uint32 result) -> bool {
|
||||
return sign<Size>(result) < 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<uint Size, bool Extend> auto M68K::ADD(uint32 source, uint32 target) -> uint32 {
|
||||
uint64 result = (uint64)source + (uint64)target;
|
||||
if(Extend) result += r.x;
|
||||
|
||||
r.c = sign<Size>(result >> 1) < 0;
|
||||
r.v = sign<Size>(~(target ^ source) & (target ^ result)) < 0;
|
||||
if(Extend == 0) r.z = clip<Size>(result) == 0;
|
||||
if(Extend == 1) if(clip<Size>(result)) r.z = 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionADD(DataRegister dr, uint1 direction, EffectiveAddress ea) -> void {
|
||||
if(direction == 0) {
|
||||
auto source = read<Size>(ea);
|
||||
auto target = read<Size>(dr);
|
||||
auto result = ADD<Size>(source, target);
|
||||
write<Size>(dr, result);
|
||||
} else {
|
||||
auto source = read<Size>(dr);
|
||||
auto target = read<Size>(ea);
|
||||
auto result = ADD<Size>(source, target);
|
||||
write<Size>(ea, result);
|
||||
}
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionADDI(EffectiveAddress modify) -> void {
|
||||
auto source = readPC<Size>();
|
||||
auto target = read<Size>(modify);
|
||||
auto result = ADD<Size>(source, target);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionADDA(AddressRegister ar, EffectiveAddress ea) -> void {
|
||||
auto source = read<Size>(ea);
|
||||
auto target = read<Size>(ar);
|
||||
write<Long>(ar, source + target);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionADDQ(uint4 immediate, EffectiveAddress modify) -> void {
|
||||
auto source = read<Size>(modify);
|
||||
auto target = immediate;
|
||||
auto result = ADD<Size>(source, target);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionADDX(EffectiveAddress target_, EffectiveAddress source_) -> void {
|
||||
auto source = read<Size>(source_);
|
||||
auto target = read<Size>(target_);
|
||||
auto result = ADD<Size, Extend>(source, target);
|
||||
write<Size>(target, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionANDI(EffectiveAddress ea) -> void {
|
||||
auto source = readPC<Size>();
|
||||
auto target = read<Size, NoUpdate>(ea);
|
||||
auto result = target & source;
|
||||
write<Size>(ea, result);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = zero<Size>(result);
|
||||
r.n = negative<Size>(result);
|
||||
}
|
||||
|
||||
auto M68K::instructionANDI_TO_CCR() -> void {
|
||||
auto data = readPC<Word>();
|
||||
writeCCR(readCCR() & data);
|
||||
}
|
||||
|
||||
auto M68K::instructionANDI_TO_SR() -> void {
|
||||
if(!supervisor()) return;
|
||||
|
||||
auto data = readPC<Word>();
|
||||
writeSR(readSR() & data);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::ASL(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = false;
|
||||
uint32 overflow = 0;
|
||||
for(auto _ : range(shift)) {
|
||||
carry = result & msb<Size>();
|
||||
uint32 before = result;
|
||||
result <<= 1;
|
||||
overflow |= before ^ result;
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = sign<Size>(overflow) < 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
if(shift) r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionASL(uint4 shift, DataRegister modify) -> void {
|
||||
auto result = ASL<Size>(read<Size>(modify), shift);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionASL(DataRegister shift, DataRegister modify) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = ASL<Size>(read<Size>(modify), count);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionASL(EffectiveAddress modify) -> void {
|
||||
auto result = ASL<Word>(read<Word, NoUpdate>(modify), 1);
|
||||
write<Word>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::ASR(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = false;
|
||||
uint32 overflow = 0;
|
||||
for(auto _ : range(shift)) {
|
||||
carry = result & lsb<Size>();
|
||||
uint32 before = result;
|
||||
result = sign<Size>(result) >> 1;
|
||||
overflow |= before ^ result;
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = sign<Size>(overflow) < 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
if(shift) r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionASR(uint4 shift, DataRegister modify) -> void {
|
||||
auto result = ASR<Size>(read<Size>(modify), shift);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionASR(DataRegister shift, DataRegister modify) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = ASR<Size>(read<Size>(modify), count);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionASR(EffectiveAddress modify) -> void {
|
||||
auto result = ASR<Word>(read<Word, NoUpdate>(modify), 1);
|
||||
write<Word>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
|
||||
auto extension = readPC<Word>();
|
||||
if(condition == 1) push<Long>(r.pc);
|
||||
if(condition >= 2 && !testCondition(condition)) { //0 = BRA; 1 = BSR
|
||||
if(displacement) r.pc -= 2;
|
||||
} else {
|
||||
r.pc -= 2;
|
||||
r.pc += displacement ? sign<Byte>(displacement) : sign<Word>(extension);
|
||||
}
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionBTST(DataRegister dr, EffectiveAddress ea) -> void {
|
||||
auto bit = read<Size>(dr);
|
||||
auto test = read<Size>(ea);
|
||||
bit &= bits<Size>() - 1;
|
||||
|
||||
r.z = test.bit(bit) == 0;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionBTST(EffectiveAddress ea) -> void {
|
||||
auto bit = (uint8)readPC<Word>();
|
||||
auto test = read<Size>(ea);
|
||||
bit &= bits<Size>() - 1;
|
||||
|
||||
r.z = test.bit(bit) == 0;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionCLR(EffectiveAddress ea) -> void {
|
||||
read<Size>(ea);
|
||||
write<Size>(ea, 0);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = 1;
|
||||
r.n = 0;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::CMP(uint32 source, uint32 target) -> uint32 {
|
||||
uint64 result = (uint64)target - (uint64)source;
|
||||
|
||||
r.c = sign<Size>(result >> 1) < 0;
|
||||
r.v = sign<Size>((target ^ source) & (target ^ result)) < 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionCMP(DataRegister dr, EffectiveAddress ea) -> void {
|
||||
auto source = read<Size>(ea);
|
||||
auto target = read<Size>(dr);
|
||||
CMP<Size>(source, target);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionCMPA(AddressRegister ar, EffectiveAddress ea) -> void {
|
||||
auto source = read<Size>(ea);
|
||||
auto target = read<Size>(ar);
|
||||
CMP<Size>(source, target);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionCMPI(EffectiveAddress ea) -> void {
|
||||
auto source = readPC<Size>();
|
||||
auto target = read<Size>(ea);
|
||||
CMP<Size>(source, target);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionCMPM(EffectiveAddress ax, EffectiveAddress ay) -> void {
|
||||
auto source = read<Size>(ay);
|
||||
auto target = read<Size>(ax);
|
||||
CMP<Size>(source, target);
|
||||
}
|
||||
|
||||
auto M68K::instructionDBCC(uint4 condition, DataRegister dr) -> void {
|
||||
auto displacement = readPC<Word>();
|
||||
if(!testCondition(condition)) {
|
||||
uint16 result = read<Word>(dr);
|
||||
write<Word>(dr, result - 1);
|
||||
if(result) r.pc -= 2, r.pc += sign<Word>(displacement);
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::instructionEORI_TO_CCR() -> void {
|
||||
auto data = readPC<Word>();
|
||||
writeCCR(readCCR() ^ data);
|
||||
}
|
||||
|
||||
auto M68K::instructionEORI_TO_SR() -> void {
|
||||
if(!supervisor()) return;
|
||||
|
||||
auto data = readPC<Word>();
|
||||
writeSR(readSR() ^ data);
|
||||
}
|
||||
|
||||
auto M68K::instructionJSR(EffectiveAddress target) -> void {
|
||||
push<Long>(r.pc);
|
||||
r.pc = fetch<Long>(target);
|
||||
}
|
||||
|
||||
auto M68K::instructionLEA(AddressRegister ar, EffectiveAddress ea) -> void {
|
||||
write<Long>(ar, fetch<Long>(ea));
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::LSL(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = false;
|
||||
for(auto _ : range(shift)) {
|
||||
carry = result & msb<Size>();
|
||||
result <<= 1;
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
if(shift) r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionLSL(uint4 immediate, DataRegister dr) -> void {
|
||||
auto result = LSL<Size>(read<Size>(dr), immediate);
|
||||
write<Size>(dr, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionLSL(DataRegister sr, DataRegister dr) -> void {
|
||||
auto shift = read<Long>(sr) & 63;
|
||||
auto result = LSL<Size>(read<Size>(dr), shift);
|
||||
write<Size>(dr, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionLSL(EffectiveAddress ea) -> void {
|
||||
auto result = LSL<Word>(read<Word, NoUpdate>(ea), 1);
|
||||
write<Word>(ea, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::LSR(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = false;
|
||||
for(auto _ : range(shift)) {
|
||||
carry = result & lsb<Size>();
|
||||
result >>= 1;
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
if(shift) r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionLSR(uint4 immediate, DataRegister dr) -> void {
|
||||
auto result = LSR<Size>(read<Size>(dr), immediate);
|
||||
write<Size>(dr, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionLSR(DataRegister shift, DataRegister dr) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = LSR<Size>(read<Size>(dr), count);
|
||||
write<Size>(dr, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionLSR(EffectiveAddress ea) -> void {
|
||||
auto result = LSR<Word>(read<Word, NoUpdate>(ea), 1);
|
||||
write<Word>(ea, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionMOVE(EffectiveAddress to, EffectiveAddress from) -> void {
|
||||
auto data = read<Size>(from);
|
||||
write<Size>(to, data);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = zero<Size>(data);
|
||||
r.n = negative<Size>(data);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionMOVEA(AddressRegister ar, EffectiveAddress ea) -> void {
|
||||
auto data = read<Size>(ea);
|
||||
write<Long>(ar, data);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionMOVEM(uint1 direction, EffectiveAddress ea) -> void {
|
||||
auto list = readPC();
|
||||
auto addr = fetch<Long>(ea);
|
||||
|
||||
for(uint n : range(16)) {
|
||||
if(!list.bit(n)) continue;
|
||||
|
||||
//pre-decrement mode traverses registers in reverse order {A7-A0, D7-D0}
|
||||
uint index = ea.mode == AddressRegisterIndirectWithPreDecrement ? 15 - n : n;
|
||||
|
||||
if(ea.mode == AddressRegisterIndirectWithPreDecrement) addr -= bytes<Size>();
|
||||
|
||||
if(direction == 0) {
|
||||
auto data = index < 8 ? read<Size>(DataRegister{index}) : read<Size>(AddressRegister{index});
|
||||
write<Size>(addr, data);
|
||||
} else {
|
||||
auto data = read<Size>(addr);
|
||||
data = sign<Size>(data);
|
||||
index < 8 ? write<Long>(DataRegister{index}, data) : write<Long>(AddressRegister{index}, data);
|
||||
}
|
||||
|
||||
if(ea.mode == AddressRegisterIndirectWithPostIncrement) addr += bytes<Size>();
|
||||
}
|
||||
|
||||
flush<Long>(ea, addr);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEQ(DataRegister dr, uint8 immediate) -> void {
|
||||
write<Long>(dr, immediate);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = zero<Byte>(immediate);
|
||||
r.n = negative<Byte>(immediate);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE_FROM_SR(EffectiveAddress ea) -> void {
|
||||
auto data = readSR();
|
||||
write<Word>(ea, data);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE_TO_CCR(EffectiveAddress ea) -> void {
|
||||
auto data = read<Byte>(ea);
|
||||
writeCCR(data);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE_TO_SR(EffectiveAddress ea) -> void {
|
||||
if(!supervisor()) return;
|
||||
|
||||
auto data = read<Word>(ea);
|
||||
writeSR(data);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE_USP(uint1 direction, AddressRegister ar) -> void {
|
||||
if(!supervisor()) return;
|
||||
|
||||
if(direction == 0) {
|
||||
r.sp = read<Long>(ar);
|
||||
} else {
|
||||
write<Long>(ar, r.sp);
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::instructionNOP() -> void {
|
||||
}
|
||||
|
||||
auto M68K::instructionORI_TO_CCR() -> void {
|
||||
auto data = readPC<Word>();
|
||||
writeCCR(readCCR() | data);
|
||||
}
|
||||
|
||||
auto M68K::instructionORI_TO_SR() -> void {
|
||||
if(!supervisor()) return;
|
||||
|
||||
auto data = readPC<Word>();
|
||||
writeSR(readSR() | data);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::ROL(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = false;
|
||||
for(auto _ : range(shift)) {
|
||||
carry = result & msb<Size>();
|
||||
result = result << 1 | carry;
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROL(uint4 shift, DataRegister modify) -> void {
|
||||
auto result = ROL<Size>(read<Size>(modify), shift);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROL(DataRegister shift, DataRegister modify) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = ROL<Size>(read<Size>(modify), count);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionROL(EffectiveAddress modify) -> void {
|
||||
auto result = ROL<Word>(read<Word, NoUpdate>(modify), 1);
|
||||
write<Word>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::ROR(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = false;
|
||||
for(auto _ : range(shift)) {
|
||||
carry = result & lsb<Size>();
|
||||
result >>= 1;
|
||||
if(carry) result |= msb<Size>();
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROR(uint4 shift, DataRegister modify) -> void {
|
||||
auto result = ROR<Size>(read<Size>(modify), shift);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROR(DataRegister shift, DataRegister modify) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = ROR<Size>(read<Size>(modify), count);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionROR(EffectiveAddress modify) -> void {
|
||||
auto result = ROR<Word>(read<Word, NoUpdate>(modify), 1);
|
||||
write<Word>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::ROXL(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = r.x;
|
||||
for(auto _ : range(shift)) {
|
||||
bool extend = carry;
|
||||
carry = result & msb<Size>();
|
||||
result = result << 1 | extend;
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROXL(uint4 shift, DataRegister modify) -> void {
|
||||
auto result = ROXL<Size>(read<Size>(modify), shift);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROXL(DataRegister shift, DataRegister modify) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = ROXL<Size>(read<Size>(modify), count);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionROXL(EffectiveAddress modify) -> void {
|
||||
auto result = ROXL<Word>(read<Word, NoUpdate>(modify), 1);
|
||||
write<Word>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::ROXR(uint32 result, uint shift) -> uint32 {
|
||||
bool carry = r.x;
|
||||
for(auto _ : range(shift)) {
|
||||
bool extend = carry;
|
||||
carry = result & lsb<Size>();
|
||||
result >>= 1;
|
||||
if(extend) result |= msb<Size>();
|
||||
}
|
||||
|
||||
r.c = carry;
|
||||
r.v = 0;
|
||||
r.z = clip<Size>(result) == 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
r.x = r.c;
|
||||
|
||||
return clip<Size>(result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROXR(uint4 shift, DataRegister modify) -> void {
|
||||
auto result = ROXR<Size>(read<Size>(modify), shift);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionROXR(DataRegister shift, DataRegister modify) -> void {
|
||||
auto count = read<Long>(shift) & 63;
|
||||
auto result = ROXR<Size>(read<Size>(modify), count);
|
||||
write<Size>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionROXR(EffectiveAddress modify) -> void {
|
||||
auto result = ROXR<Word>(read<Word, NoUpdate>(modify), 1);
|
||||
write<Word>(modify, result);
|
||||
}
|
||||
|
||||
auto M68K::instructionRTS() -> void {
|
||||
r.pc = pop<Long>();
|
||||
}
|
||||
|
||||
template<uint Size, bool Extend> auto M68K::SUB(uint32 source, uint32 target) -> uint32 {
|
||||
uint64 result = source - target;
|
||||
if(Extend) result -= r.x;
|
||||
|
||||
r.c = sign<Size>(result >> 1) < 0;
|
||||
r.v = sign<Size>((target ^ source) & (target ^ result)) < 0;
|
||||
if(Extend == 0) r.z = clip<Size>(result == 0);
|
||||
if(Extend == 1) if(clip<Size>(result)) r.z = 0;
|
||||
r.n = sign<Size>(result) < 0;
|
||||
r.x = r.c;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionSUB(EffectiveAddress source_, DataRegister target_) -> void {
|
||||
auto source = read<Size>(source_);
|
||||
auto target = read<Size>(target_);
|
||||
auto result = SUB<Size>(source, target);
|
||||
write<Size>(target_, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionSUB(DataRegister source_, EffectiveAddress target_) -> void {
|
||||
auto source = read<Size>(source_);
|
||||
auto target = read<Size>(target_);
|
||||
auto result = SUB<Size>(source, target);
|
||||
write<Size>(target_, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionSUBQ(uint4 immediate, EffectiveAddress ea) -> void {
|
||||
auto source = immediate;
|
||||
auto target = read<Size, NoUpdate>(ea);
|
||||
auto result = SUB<Size>(source, target);
|
||||
write<Size>(ea, result);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::instructionTST(EffectiveAddress ea) -> void {
|
||||
auto data = read<Size>(ea);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = zero<Size>(data);
|
||||
r.n = negative<Size>(data);
|
||||
}
|
43
higan/processor/m68k/m68k.cpp
Normal file
43
higan/processor/m68k/m68k.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <processor/processor.hpp>
|
||||
#include "m68k.hpp"
|
||||
|
||||
namespace Processor {
|
||||
|
||||
enum : uint { Byte, Word, Long };
|
||||
enum : bool { Reverse = 1 };
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "effective-address.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "instructions.cpp"
|
||||
#include "disassembler.cpp"
|
||||
|
||||
auto M68K::power() -> void {
|
||||
}
|
||||
|
||||
auto M68K::reset() -> void {
|
||||
instructionsExecuted = 0;
|
||||
|
||||
for(auto& dr : r.d) dr = 0;
|
||||
for(auto& ar : r.a) ar = 0;
|
||||
r.sp = 0;
|
||||
r.pc = 0;
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = 0;
|
||||
r.n = 0;
|
||||
r.x = 0;
|
||||
r.i = 7;
|
||||
r.s = 1;
|
||||
r.t = 0;
|
||||
}
|
||||
|
||||
auto M68K::supervisor() -> bool {
|
||||
if(r.s) return true;
|
||||
//todo: raise TRAP exception
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
277
higan/processor/m68k/m68k.hpp
Normal file
277
higan/processor/m68k/m68k.hpp
Normal file
@@ -0,0 +1,277 @@
|
||||
#pragma once
|
||||
|
||||
//Motorola M68000
|
||||
|
||||
namespace Processor {
|
||||
|
||||
struct M68K {
|
||||
enum : bool { User, Supervisor };
|
||||
enum : uint { Byte, Word, Long };
|
||||
enum : bool { NoUpdate = 0, Reverse = 1, Extend = 1 };
|
||||
|
||||
enum : uint {
|
||||
DataRegisterDirect,
|
||||
AddressRegisterDirect,
|
||||
AddressRegisterIndirect,
|
||||
AddressRegisterIndirectWithPostIncrement,
|
||||
AddressRegisterIndirectWithPreDecrement,
|
||||
AddressRegisterIndirectWithDisplacement,
|
||||
AddressRegisterIndirectWithIndex,
|
||||
AbsoluteShortIndirect,
|
||||
AbsoluteLongIndirect,
|
||||
ProgramCounterIndirectWithDisplacement,
|
||||
ProgramCounterIndirectWithIndex,
|
||||
Immediate,
|
||||
};
|
||||
|
||||
M68K();
|
||||
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
virtual auto read(bool word, uint24 addr) -> uint16 = 0;
|
||||
virtual auto write(bool word, uint24 addr, uint16 data) -> void = 0;
|
||||
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
auto supervisor() -> bool;
|
||||
|
||||
//registers.cpp
|
||||
struct DataRegister {
|
||||
explicit DataRegister(uint number_) : number(number_) {}
|
||||
uint3 number;
|
||||
};
|
||||
template<uint Size = Long> auto read(DataRegister reg) -> uint32;
|
||||
template<uint Size = Long> auto write(DataRegister reg, uint32 data) -> void;
|
||||
|
||||
struct AddressRegister {
|
||||
explicit AddressRegister(uint number_) : number(number_) {}
|
||||
uint3 number;
|
||||
};
|
||||
template<uint Size = Long> auto read(AddressRegister reg) -> uint32;
|
||||
template<uint Size = Long> auto write(AddressRegister reg, uint32 data) -> void;
|
||||
|
||||
auto readCCR() -> uint8;
|
||||
auto readSR() -> uint16;
|
||||
auto writeCCR(uint8 ccr) -> void;
|
||||
auto writeSR(uint16 sr) -> void;
|
||||
|
||||
//memory.cpp
|
||||
template<uint Size> auto read(uint32 addr) -> uint32;
|
||||
template<uint Size, bool Order = 0> auto write(uint32 addr, uint32 data) -> void;
|
||||
template<uint Size = Word> auto readPC() -> uint32;
|
||||
template<uint Size> auto pop() -> uint32;
|
||||
template<uint Size> auto push(uint32 data) -> void;
|
||||
|
||||
//effective-address.cpp
|
||||
struct EffectiveAddress {
|
||||
explicit EffectiveAddress(uint mode_, uint reg_) : mode(mode_), reg(reg_) {
|
||||
if(mode == 7) mode += reg; //optimization: convert modes {7; 0-4} to {8-11}
|
||||
}
|
||||
|
||||
uint4 mode;
|
||||
uint3 reg;
|
||||
|
||||
boolean valid;
|
||||
uint32 address;
|
||||
};
|
||||
|
||||
template<uint Size> auto fetch(EffectiveAddress& ea) -> uint32;
|
||||
template<uint Size, bool Update = 1> auto read(EffectiveAddress& ea) -> uint32;
|
||||
template<uint Size, bool Update = 1> auto write(EffectiveAddress& ea, uint32 data) -> void;
|
||||
template<uint Size> auto flush(EffectiveAddress& ea, uint32 data) -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto trap() -> void;
|
||||
auto instruction() -> void;
|
||||
|
||||
//instructions.cpp
|
||||
auto testCondition(uint4 condition) -> bool;
|
||||
|
||||
template<uint Size> auto bytes() -> uint;
|
||||
template<uint Size> auto bits() -> uint;
|
||||
template<uint Size> auto lsb() -> uint32;
|
||||
template<uint Size> auto msb() -> uint32;
|
||||
template<uint Size> auto mask() -> uint32;
|
||||
template<uint Size> auto clip(uint32 data) -> uint32;
|
||||
template<uint Size> auto sign(uint32 data) -> int32;
|
||||
|
||||
template<uint Size> auto zero(uint32 result) -> bool;
|
||||
template<uint Size> auto negative(uint32 result) -> bool;
|
||||
|
||||
template<uint Size, bool Extend = false> auto ADD(uint32 source, uint32 target) -> uint32;
|
||||
template<uint Size> auto instructionADD(DataRegister dr, uint1 direction, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionADDA(AddressRegister ar, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionADDI(EffectiveAddress modify) -> void;
|
||||
template<uint Size> auto instructionADDQ(uint4 immediate, EffectiveAddress modify) -> void;
|
||||
template<uint Size> auto instructionADDX(EffectiveAddress target, EffectiveAddress source) -> void;
|
||||
template<uint Size> auto instructionANDI(EffectiveAddress ea) -> void;
|
||||
auto instructionANDI_TO_CCR() -> void;
|
||||
auto instructionANDI_TO_SR() -> void;
|
||||
template<uint Size> auto ASL(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionASL(uint4 shift, DataRegister modify) -> void;
|
||||
template<uint Size> auto instructionASL(DataRegister shift, DataRegister modify) -> void;
|
||||
auto instructionASL(EffectiveAddress modify) -> void;
|
||||
template<uint Size> auto ASR(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionASR(uint4 shift, DataRegister modify) -> void;
|
||||
template<uint Size> auto instructionASR(DataRegister shift, DataRegister modify) -> void;
|
||||
auto instructionASR(EffectiveAddress modify) -> void;
|
||||
auto instructionBCC(uint4 condition, uint8 displacement) -> void;
|
||||
template<uint Size> auto instructionBTST(DataRegister dr, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionBTST(EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionCLR(EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto CMP(uint32 source, uint32 target) -> uint32;
|
||||
template<uint Size> auto instructionCMP(DataRegister dr, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionCMPA(AddressRegister ar, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionCMPI(EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionCMPM(EffectiveAddress ax, EffectiveAddress ay) -> void;
|
||||
auto instructionDBCC(uint4 condition, DataRegister dr) -> void;
|
||||
auto instructionEORI_TO_CCR() -> void;
|
||||
auto instructionEORI_TO_SR() -> void;
|
||||
auto instructionJSR(EffectiveAddress target) -> void;
|
||||
auto instructionLEA(AddressRegister ar, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto LSL(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionLSL(uint4 immediate, DataRegister dr) -> void;
|
||||
template<uint Size> auto instructionLSL(DataRegister sr, DataRegister dr) -> void;
|
||||
auto instructionLSL(EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto LSR(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionLSR(uint4 immediate, DataRegister dr) -> void;
|
||||
template<uint Size> auto instructionLSR(DataRegister shift, DataRegister dr) -> void;
|
||||
auto instructionLSR(EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionMOVE(EffectiveAddress to, EffectiveAddress from) -> void;
|
||||
template<uint Size> auto instructionMOVEA(AddressRegister ar, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionMOVEM(uint1 direction, EffectiveAddress ea) -> void;
|
||||
auto instructionMOVEQ(DataRegister dr, uint8 immediate) -> void;
|
||||
auto instructionMOVE_FROM_SR(EffectiveAddress ea) -> void;
|
||||
auto instructionMOVE_TO_CCR(EffectiveAddress ea) -> void;
|
||||
auto instructionMOVE_TO_SR(EffectiveAddress ea) -> void;
|
||||
auto instructionMOVE_USP(uint1 direction, AddressRegister ar) -> void;
|
||||
auto instructionNOP() -> void;
|
||||
auto instructionORI_TO_CCR() -> void;
|
||||
auto instructionORI_TO_SR() -> void;
|
||||
template<uint Size> auto ROL(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionROL(uint4 shift, DataRegister modify) -> void;
|
||||
template<uint Size> auto instructionROL(DataRegister shift, DataRegister modify) -> void;
|
||||
auto instructionROL(EffectiveAddress modify) -> void;
|
||||
template<uint Size> auto ROR(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionROR(uint4 shift, DataRegister modify) -> void;
|
||||
template<uint Size> auto instructionROR(DataRegister shift, DataRegister modify) -> void;
|
||||
auto instructionROR(EffectiveAddress modify) -> void;
|
||||
template<uint Size> auto ROXL(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionROXL(uint4 shift, DataRegister modify) -> void;
|
||||
template<uint Size> auto instructionROXL(DataRegister shift, DataRegister modify) -> void;
|
||||
auto instructionROXL(EffectiveAddress modify) -> void;
|
||||
template<uint Size> auto ROXR(uint32 result, uint shift) -> uint32;
|
||||
template<uint Size> auto instructionROXR(uint4 shift, DataRegister modify) -> void;
|
||||
template<uint Size> auto instructionROXR(DataRegister shift, DataRegister modify) -> void;
|
||||
auto instructionROXR(EffectiveAddress modify) -> void;
|
||||
auto instructionRTS() -> void;
|
||||
template<uint Size, bool Extend = false> auto SUB(uint32 source, uint32 target) -> uint32;
|
||||
template<uint Size> auto instructionSUB(EffectiveAddress source, DataRegister target) -> void;
|
||||
template<uint Size> auto instructionSUB(DataRegister source, EffectiveAddress target) -> void;
|
||||
template<uint Size> auto instructionSUBQ(uint4 immediate, EffectiveAddress ea) -> void;
|
||||
template<uint Size> auto instructionTST(EffectiveAddress ea) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(uint32 pc) -> string;
|
||||
auto disassembleRegisters() -> string;
|
||||
|
||||
struct Registers {
|
||||
uint32 d[8];
|
||||
uint32 a[8];
|
||||
uint32 sp;
|
||||
uint32 pc;
|
||||
|
||||
bool c; //carry
|
||||
bool v; //overflow
|
||||
bool z; //zero
|
||||
bool n; //negative
|
||||
bool x; //extend
|
||||
uint3 i; //interrupt mask
|
||||
bool s; //supervisor mode
|
||||
bool t; //trace mode
|
||||
} r;
|
||||
|
||||
uint16 opcode = 0;
|
||||
uint instructionsExecuted = 0;
|
||||
|
||||
function<void ()> instructionTable[65536];
|
||||
|
||||
private:
|
||||
//disassembler.cpp
|
||||
template<uint Size> auto disassembleADD(DataRegister dr, uint1 direction, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleADDA(AddressRegister ar, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleADDI(EffectiveAddress modify) -> string;
|
||||
template<uint Size> auto disassembleADDQ(uint4 immediate, EffectiveAddress modify) -> string;
|
||||
template<uint Size> auto disassembleADDX(EffectiveAddress target, EffectiveAddress source) -> string;
|
||||
template<uint Size> auto disassembleANDI(EffectiveAddress ea) -> string;
|
||||
auto disassembleANDI_TO_CCR() -> string;
|
||||
auto disassembleANDI_TO_SR() -> string;
|
||||
template<uint Size> auto disassembleASL(uint4 shift, DataRegister modify) -> string;
|
||||
template<uint Size> auto disassembleASL(DataRegister shift, DataRegister modify) -> string;
|
||||
auto disassembleASL(EffectiveAddress modify) -> string;
|
||||
template<uint Size> auto disassembleASR(uint4 shift, DataRegister modify) -> string;
|
||||
template<uint Size> auto disassembleASR(DataRegister shift, DataRegister modify) -> string;
|
||||
auto disassembleASR(EffectiveAddress modify) -> string;
|
||||
auto disassembleBCC(uint4 condition, uint8 displacement) -> string;
|
||||
template<uint Size> auto disassembleBTST(DataRegister dr, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleBTST(EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleCLR(EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleCMP(DataRegister dr, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleCMPA(AddressRegister ar, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleCMPI(EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleCMPM(EffectiveAddress ax, EffectiveAddress ay) -> string;
|
||||
auto disassembleDBCC(uint4 condition, DataRegister dr) -> string;
|
||||
auto disassembleEORI_TO_CCR() -> string;
|
||||
auto disassembleEORI_TO_SR() -> string;
|
||||
auto disassembleJSR(EffectiveAddress target) -> string;
|
||||
auto disassembleLEA(AddressRegister ar, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleLSL(uint4 immediate, DataRegister dr) -> string;
|
||||
template<uint Size> auto disassembleLSL(DataRegister sr, DataRegister dr) -> string;
|
||||
auto disassembleLSL(EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleLSR(uint4 immediate, DataRegister dr) -> string;
|
||||
template<uint Size> auto disassembleLSR(DataRegister shift, DataRegister dr) -> string;
|
||||
auto disassembleLSR(EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleMOVE(EffectiveAddress to, EffectiveAddress from) -> string;
|
||||
template<uint Size> auto disassembleMOVEA(AddressRegister ar, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleMOVEM(uint1 direction, EffectiveAddress ea) -> string;
|
||||
auto disassembleMOVEQ(DataRegister dr, uint8 immediate) -> string;
|
||||
auto disassembleMOVE_FROM_SR(EffectiveAddress ea) -> string;
|
||||
auto disassembleMOVE_TO_CCR(EffectiveAddress ea) -> string;
|
||||
auto disassembleMOVE_TO_SR(EffectiveAddress ea) -> string;
|
||||
auto disassembleMOVE_USP(uint1 direction, AddressRegister ar) -> string;
|
||||
auto disassembleNOP() -> string;
|
||||
auto disassembleORI_TO_CCR() -> string;
|
||||
auto disassembleORI_TO_SR() -> string;
|
||||
template<uint Size> auto disassembleROL(uint4 shift, DataRegister modify) -> string;
|
||||
template<uint Size> auto disassembleROL(DataRegister shift, DataRegister modify) -> string;
|
||||
auto disassembleROL(EffectiveAddress modify) -> string;
|
||||
template<uint Size> auto disassembleROR(uint4 shift, DataRegister modify) -> string;
|
||||
template<uint Size> auto disassembleROR(DataRegister shift, DataRegister modify) -> string;
|
||||
auto disassembleROR(EffectiveAddress modify) -> string;
|
||||
template<uint Size> auto disassembleROXL(uint4 shift, DataRegister modify) -> string;
|
||||
template<uint Size> auto disassembleROXL(DataRegister shift, DataRegister modify) -> string;
|
||||
auto disassembleROXL(EffectiveAddress modify) -> string;
|
||||
template<uint Size> auto disassembleROXR(uint4 shift, DataRegister modify) -> string;
|
||||
template<uint Size> auto disassembleROXR(DataRegister shift, DataRegister modify) -> string;
|
||||
auto disassembleROXR(EffectiveAddress modify) -> string;
|
||||
auto disassembleRTS() -> string;
|
||||
template<uint Size> auto disassembleSUB(EffectiveAddress source, DataRegister target) -> string;
|
||||
template<uint Size> auto disassembleSUB(DataRegister source, EffectiveAddress target) -> string;
|
||||
template<uint Size> auto disassembleSUBQ(uint4 immediate, EffectiveAddress ea) -> string;
|
||||
template<uint Size> auto disassembleTST(EffectiveAddress ea) -> string;
|
||||
|
||||
template<uint Size> auto _read(uint32 addr) -> uint32;
|
||||
template<uint Size = Word> auto _readPC() -> uint32;
|
||||
auto _dataRegister(DataRegister dr) -> string;
|
||||
auto _addressRegister(AddressRegister ar) -> string;
|
||||
template<uint Size> auto _immediate() -> string;
|
||||
template<uint Size> auto _address(EffectiveAddress& ea) -> string;
|
||||
template<uint Size> auto _effectiveAddress(EffectiveAddress& ea) -> string;
|
||||
auto _branch(uint8 displacement) -> string;
|
||||
template<uint Size> auto _suffix() -> string;
|
||||
auto _condition(uint4 condition) -> string;
|
||||
|
||||
uint32 _pc;
|
||||
function<string ()> disassembleTable[65536];
|
||||
};
|
||||
|
||||
}
|
91
higan/processor/m68k/memory.cpp
Normal file
91
higan/processor/m68k/memory.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
template<> auto M68K::read<Byte>(uint32 addr) -> uint32 {
|
||||
step(4);
|
||||
return read(0, addr);
|
||||
}
|
||||
|
||||
template<> auto M68K::read<Word>(uint32 addr) -> uint32 {
|
||||
step(4);
|
||||
return read(1, addr);
|
||||
}
|
||||
|
||||
template<> auto M68K::read<Long>(uint32 addr) -> uint32 {
|
||||
step(4);
|
||||
uint32 data = read(1, addr + 0) << 16;
|
||||
step(4);
|
||||
return data | read(1, addr + 2) << 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<> auto M68K::write<Byte>(uint32 addr, uint32 data) -> void {
|
||||
step(4);
|
||||
return write(0, addr, data);
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Word>(uint32 addr, uint32 data) -> void {
|
||||
step(4);
|
||||
return write(1, addr, data);
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Long>(uint32 addr, uint32 data) -> void {
|
||||
step(4);
|
||||
write(1, addr + 0, data >> 16);
|
||||
step(4);
|
||||
write(1, addr + 2, data >> 0);
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Byte, Reverse>(uint32 addr, uint32 data) -> void {
|
||||
step(4);
|
||||
return write(0, addr, data);
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Word, Reverse>(uint32 addr, uint32 data) -> void {
|
||||
step(4);
|
||||
return write(1, addr, data);
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Long, Reverse>(uint32 addr, uint32 data) -> void {
|
||||
step(4);
|
||||
write(1, addr + 2, data >> 0);
|
||||
step(4);
|
||||
write(1, addr + 0, data >> 16);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<> auto M68K::readPC<Byte>() -> uint32 {
|
||||
step(4);
|
||||
auto data = read(1, r.pc);
|
||||
r.pc += 2;
|
||||
return (uint8)data;
|
||||
}
|
||||
|
||||
template<> auto M68K::readPC<Word>() -> uint32 {
|
||||
step(4);
|
||||
auto data = read(1, r.pc);
|
||||
r.pc += 2;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto M68K::readPC<Long>() -> uint32 {
|
||||
step(4);
|
||||
auto hi = read(1, r.pc);
|
||||
r.pc += 2;
|
||||
step(4);
|
||||
auto lo = read(1, r.pc);
|
||||
r.pc += 2;
|
||||
return hi << 16 | lo << 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<uint Size> auto M68K::pop() -> uint32 {
|
||||
auto data = read<Size>((uint32)r.a[7]);
|
||||
r.a[7] += bytes<Size>();
|
||||
return data;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::push(uint32 data) -> void {
|
||||
r.a[7] -= bytes<Size>();
|
||||
return write<Size, Reverse>((uint32)r.a[7], data);
|
||||
}
|
48
higan/processor/m68k/registers.cpp
Normal file
48
higan/processor/m68k/registers.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
template<uint Size> auto M68K::read(DataRegister reg) -> uint32 {
|
||||
return clip<Size>(r.d[reg.number]);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::write(DataRegister reg, uint32 data) -> void {
|
||||
r.d[reg.number] = (r.d[reg.number] & ~mask<Size>()) | (data & mask<Size>());
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<uint Size> auto M68K::read(AddressRegister reg) -> uint32 {
|
||||
return sign<Size>(r.a[reg.number]);
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::write(AddressRegister reg, uint32 data) -> void {
|
||||
r.a[reg.number] = sign<Size>(data);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
//CCR,SR unused bits cannot be set; always read out as 0
|
||||
|
||||
auto M68K::readCCR() -> uint8 {
|
||||
return r.c << 0 | r.v << 1 | r.z << 2 | r.n << 3 | r.x << 4;
|
||||
}
|
||||
|
||||
auto M68K::readSR() -> uint16 {
|
||||
return readCCR() << 0 | r.i << 8 | r.s << 13 | r.t << 15;
|
||||
}
|
||||
|
||||
auto M68K::writeCCR(uint8 ccr) -> void {
|
||||
r.c = ccr.bit(0);
|
||||
r.v = ccr.bit(1);
|
||||
r.z = ccr.bit(2);
|
||||
r.n = ccr.bit(3);
|
||||
r.x = ccr.bit(4);
|
||||
}
|
||||
|
||||
auto M68K::writeSR(uint16 sr) -> void {
|
||||
writeCCR(sr);
|
||||
|
||||
//when entering or exiting supervisor mode; swap SSP and USP into A7
|
||||
if(r.s != sr.bit(13)) swap(r.a[7], r.sp);
|
||||
|
||||
r.i = sr.bits(8,10);
|
||||
r.s = sr.bit(13);
|
||||
r.t = sr.bit(15);
|
||||
}
|
@@ -19,6 +19,20 @@ namespace Processor {
|
||||
#include "instructions-misc.cpp"
|
||||
#include "switch.cpp"
|
||||
|
||||
auto R65816::interrupt() -> void {
|
||||
read(r.pc.d);
|
||||
idle();
|
||||
N writeSP(r.pc.b);
|
||||
writeSP(r.pc.h);
|
||||
writeSP(r.pc.l);
|
||||
writeSP(r.e ? (r.p & ~0x10) : r.p);
|
||||
r.pc.l = read(r.vector + 0);
|
||||
r.p.i = 1;
|
||||
r.p.d = 0;
|
||||
r.pc.h = read(r.vector + 1);
|
||||
r.pc.b = 0x00;
|
||||
}
|
||||
|
||||
//immediate, 2-cycle opcodes with idle cycle will become bus read
|
||||
//when an IRQ is to be triggered immediately after opcode completion.
|
||||
//this affects the following opcodes:
|
||||
@@ -54,20 +68,6 @@ auto R65816::idle6(uint16 addr) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto R65816::interrupt() -> void {
|
||||
read(r.pc.d);
|
||||
idle();
|
||||
N writeSP(r.pc.b);
|
||||
writeSP(r.pc.h);
|
||||
writeSP(r.pc.l);
|
||||
writeSP(r.e ? (r.p & ~0x10) : r.p);
|
||||
r.pc.l = read(r.vector + 0);
|
||||
r.p.i = 1;
|
||||
r.p.d = 0;
|
||||
r.pc.h = read(r.vector + 1);
|
||||
r.pc.b = 0x00;
|
||||
}
|
||||
|
||||
#undef E
|
||||
#undef N
|
||||
#undef L
|
||||
|
@@ -18,6 +18,7 @@ struct R65816 {
|
||||
virtual auto write(uint24 addr, uint8 data) -> void = 0;
|
||||
virtual auto lastCycle() -> void = 0;
|
||||
virtual auto interruptPending() const -> bool = 0;
|
||||
virtual auto interrupt() -> void;
|
||||
|
||||
virtual auto readDisassembler(uint24 addr) -> uint8 { return 0; }
|
||||
|
||||
@@ -26,7 +27,6 @@ struct R65816 {
|
||||
alwaysinline auto idle2() -> void;
|
||||
alwaysinline auto idle4(uint16 x, uint16 y) -> void;
|
||||
alwaysinline auto idle6(uint16 addr) -> void;
|
||||
auto interrupt() -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
auto op_adc_b();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
auto SPC700::serialize(serializer& s) -> void {
|
||||
s.integer(regs.pc);
|
||||
s.integer(regs.pc.w);
|
||||
s.integer(regs.a);
|
||||
s.integer(regs.x);
|
||||
s.integer(regs.y);
|
||||
|
@@ -53,7 +53,7 @@ auto V30MZ::opGroup3MemImm(Size size) {
|
||||
auto mem = getMem(size);
|
||||
switch(modrm.reg) {
|
||||
case 0: alAnd(size, mem, fetch(size)); break;
|
||||
case 1: debug("[V30MZ] GRP3.1"); break;
|
||||
case 1: warning("[V30MZ] GRP3.1"); break;
|
||||
case 2: wait(2); setMem(size, alNot(size, mem)); break;
|
||||
case 3: wait(2); setMem(size, alNeg(size, mem)); break;
|
||||
case 4: wait(2); setAcc(size * 2, alMul(size, getAcc(size), mem)); break;
|
||||
@@ -77,13 +77,13 @@ auto V30MZ::opGroup4MemImm(Size size) {
|
||||
setMem(size, alDec(size, getMem(size)));
|
||||
break;
|
||||
case 2:
|
||||
if(size == Byte) { debug("[V30MZ] GRP4.2"); break; }
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.2"); break; }
|
||||
wait(5);
|
||||
push(r.ip);
|
||||
r.ip = getMem(Word);
|
||||
break;
|
||||
case 3:
|
||||
if(size == Byte) { debug("[V30MZ] GRP4.3"); break; }
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.3"); break; }
|
||||
wait(11);
|
||||
push(r.cs);
|
||||
push(r.ip);
|
||||
@@ -91,23 +91,23 @@ auto V30MZ::opGroup4MemImm(Size size) {
|
||||
r.cs = getMem(Word, 2);
|
||||
break;
|
||||
case 4:
|
||||
if(size == Byte) { debug("[V30MZ] GRP4.4"); break; }
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.4"); break; }
|
||||
wait(4);
|
||||
r.ip = getMem(Word);
|
||||
break;
|
||||
case 5:
|
||||
if(size == Byte) { debug("[V30MZ] GRP4.5"); break; }
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.5"); break; }
|
||||
wait(9);
|
||||
r.ip = getMem(Word, 0);
|
||||
r.cs = getMem(Word, 2);
|
||||
break;
|
||||
case 6:
|
||||
if(size == Byte) { debug("[V30MZ] GRP4.6"); break; }
|
||||
if(size == Byte) { warning("[V30MZ] GRP4.6"); break; }
|
||||
wait(1);
|
||||
push(getMem(Word));
|
||||
break;
|
||||
case 7:
|
||||
debug("[V30MZ] GRP4.7");
|
||||
warning("[V30MZ] GRP4.7");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ namespace Processor {
|
||||
#include "serialization.cpp"
|
||||
#include "disassembler.cpp"
|
||||
|
||||
auto V30MZ::debug(string text) -> void {
|
||||
auto V30MZ::warning(string text) -> void {
|
||||
//print(text, "\n");
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ struct V30MZ {
|
||||
virtual auto in(uint16 port) -> uint8 = 0;
|
||||
virtual auto out(uint16 port, uint8 data) -> void = 0;
|
||||
|
||||
auto debug(string text) -> void;
|
||||
auto warning(string text) -> void;
|
||||
auto power() -> void;
|
||||
auto exec() -> void;
|
||||
auto interrupt(uint8 vector) -> void;
|
||||
|
5
higan/processor/z80/z80.cpp
Normal file
5
higan/processor/z80/z80.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include <processor/processor.hpp>
|
||||
|
||||
namespace Processor {
|
||||
|
||||
}
|
10
higan/processor/z80/z80.hpp
Normal file
10
higan/processor/z80/z80.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
//Zilog Z80
|
||||
|
||||
namespace Processor {
|
||||
|
||||
struct Z80 {
|
||||
};
|
||||
|
||||
}
|
BIN
higan/resource/logo/higan.png
Normal file
BIN
higan/resource/logo/higan.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
@@ -1,4 +1,6 @@
|
||||
namespace name=Resource
|
||||
namespace name=Logo
|
||||
binary name=higan file=logo/higan.png
|
||||
namespace name=Sprite
|
||||
binary name=CrosshairRed file=sprite/crosshair-red.png
|
||||
binary name=CrosshairGreen file=sprite/crosshair-green.png
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,7 @@
|
||||
namespace Resource {
|
||||
namespace Logo {
|
||||
extern const nall::vector<uint8_t> higan;
|
||||
}
|
||||
namespace Sprite {
|
||||
extern const nall::vector<uint8_t> CrosshairRed;
|
||||
extern const nall::vector<uint8_t> CrosshairGreen;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
processors += r65816 spc700 arm gsu hg51b upd96050
|
||||
|
||||
objects += sfc-interface sfc-system sfc-scheduler sfc-controller
|
||||
objects += sfc-cartridge sfc-cheat
|
||||
objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu
|
||||
objects += sfc-interface sfc-system sfc-controller
|
||||
objects += sfc-cartridge sfc-memory
|
||||
objects += sfc-cpu sfc-smp sfc-dsp sfc-ppu
|
||||
objects += sfc-expansion sfc-satellaview sfc-superdisc
|
||||
objects += sfc-21fx
|
||||
objects += sfc-icd2 sfc-mcc sfc-nss sfc-event
|
||||
@@ -15,10 +15,8 @@ objects += sfc-bsmemory sfc-sufamiturbo
|
||||
|
||||
obj/sfc-interface.o: sfc/interface/interface.cpp $(call rwildcard,sfc/interface)
|
||||
obj/sfc-system.o: sfc/system/system.cpp $(call rwildcard,sfc/system/)
|
||||
obj/sfc-scheduler.o: sfc/scheduler/scheduler.cpp $(call rwildcard,sfc/scheduler/)
|
||||
obj/sfc-controller.o: sfc/controller/controller.cpp $(call rwildcard,sfc/controller/)
|
||||
obj/sfc-cartridge.o: sfc/cartridge/cartridge.cpp $(call rwildcard,sfc/cartridge/)
|
||||
obj/sfc-cheat.o: sfc/cheat/cheat.cpp $(call rwildcard,sfc/cheat/)
|
||||
obj/sfc-memory.o: sfc/memory/memory.cpp $(call rwildcard,sfc/memory/)
|
||||
|
||||
obj/sfc-cpu.o: sfc/cpu/cpu.cpp $(call rwildcard,sfc/cpu/)
|
||||
|
@@ -10,7 +10,7 @@ auto Cartridge::loadCartridge(Markup::Node node) -> void {
|
||||
}
|
||||
}
|
||||
if(board["sufamiturbo"]) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo - Slot A", "st")) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo", "st")) {
|
||||
sufamiturboA.pathID = pathID();
|
||||
loadSufamiTurboA();
|
||||
}
|
||||
@@ -55,7 +55,7 @@ auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void {
|
||||
loadMemory(sufamiturboA.ram, node["board/ram"], File::Optional, sufamiturboA.pathID);
|
||||
|
||||
if(node["board/linkable"]) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo - Slot B", "st")) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo", "st")) {
|
||||
sufamiturboB.pathID = pathID();
|
||||
loadSufamiTurboB();
|
||||
}
|
||||
@@ -199,7 +199,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
|
||||
has.HitachiDSP = true;
|
||||
|
||||
hitachidsp.Frequency = node["frequency"].natural();
|
||||
if(hitachidsp.Frequency == 0) hitachidsp.frequency = 20000000;
|
||||
if(hitachidsp.Frequency == 0) hitachidsp.Frequency = 20'000'000;
|
||||
hitachidsp.Roms = roms; //1 or 2
|
||||
|
||||
loadMemory(hitachidsp.rom, node["rom"], File::Required);
|
||||
@@ -224,8 +224,8 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
|
||||
auto Cartridge::loadNECDSP(Markup::Node node) -> void {
|
||||
has.NECDSP = true;
|
||||
|
||||
necdsp.frequency = node["frequency"].natural();
|
||||
if(necdsp.frequency == 0) necdsp.frequency = 8000000;
|
||||
necdsp.Frequency = node["frequency"].natural();
|
||||
if(necdsp.Frequency == 0) necdsp.Frequency = 8000000;
|
||||
necdsp.revision
|
||||
= node["model"].text() == "uPD7725" ? NECDSP::Revision::uPD7725
|
||||
: node["model"].text() == "uPD96050" ? NECDSP::Revision::uPD96050
|
||||
|
@@ -1,32 +0,0 @@
|
||||
#include <sfc/sfc.hpp>
|
||||
|
||||
namespace SuperFamicom {
|
||||
|
||||
Cheat cheat;
|
||||
|
||||
auto Cheat::reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint data) -> void {
|
||||
codes.append({addr, Unused, data});
|
||||
}
|
||||
|
||||
auto Cheat::append(uint addr, uint comp, uint data) -> void {
|
||||
codes.append({addr, comp, data});
|
||||
}
|
||||
|
||||
auto Cheat::find(uint addr, uint comp) -> maybe<unsigned> {
|
||||
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
|
||||
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);
|
||||
|
||||
for(auto& code : codes) {
|
||||
if(code.addr == addr && (code.comp == Unused || code.comp == comp)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
|
||||
return nothing;
|
||||
}
|
||||
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
struct Cheat {
|
||||
enum : uint { Unused = ~0u };
|
||||
|
||||
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||
|
||||
auto reset() -> void;
|
||||
auto append(uint addr, uint data) -> void;
|
||||
auto append(uint addr, uint comp, uint data) -> void;
|
||||
auto find(uint addr, uint comp) -> maybe<uint>;
|
||||
|
||||
struct Code {
|
||||
uint addr;
|
||||
uint comp;
|
||||
uint data;
|
||||
};
|
||||
vector<Code> codes;
|
||||
};
|
||||
|
||||
extern Cheat cheat;
|
@@ -9,22 +9,24 @@ namespace SuperFamicom {
|
||||
#include "justifier/justifier.cpp"
|
||||
|
||||
Controller::Controller(bool port) : port(port) {
|
||||
if(!thread) create(Controller::Enter, 1);
|
||||
if(!handle()) create(Controller::Enter, 1);
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
||||
if(peripherals.controllerPort1->active()) peripherals.controllerPort1->main();
|
||||
if(peripherals.controllerPort2->active()) peripherals.controllerPort2->main();
|
||||
}
|
||||
}
|
||||
|
||||
auto Controller::main() -> void {
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Controller::iobit() -> bool {
|
||||
|
@@ -11,7 +11,7 @@
|
||||
// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read
|
||||
// 7: gnd
|
||||
|
||||
struct Controller : Cothread {
|
||||
struct Controller : Thread {
|
||||
enum : bool { Port1 = 0, Port2 = 1 };
|
||||
|
||||
Controller(bool port);
|
||||
|
@@ -76,6 +76,7 @@ auto Justifier::main() -> void {
|
||||
|
||||
prev = next;
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Justifier::data() -> uint2 {
|
||||
|
@@ -66,6 +66,7 @@ auto SuperScope::main() -> void {
|
||||
|
||||
prev = next;
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto SuperScope::data() -> uint2 {
|
||||
|
@@ -42,7 +42,7 @@ auto ArmDSP::main() -> void {
|
||||
print(disassembleRegisters(), "\n");
|
||||
print(disassembleInstructionARM(pipeline.execute.address), "\n");
|
||||
print("Executed: ", instructions, "\n");
|
||||
while(true) step(frequency);
|
||||
while(true) step(21'477'272);
|
||||
}
|
||||
|
||||
stepARM();
|
||||
@@ -50,8 +50,8 @@ auto ArmDSP::main() -> void {
|
||||
|
||||
auto ArmDSP::step(uint clocks) -> void {
|
||||
if(bridge.timer && --bridge.timer == 0);
|
||||
Cothread::step(clocks);
|
||||
synchronizeCPU();
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
//MMIO: 00-3f,80-bf:3800-38ff
|
||||
@@ -59,7 +59,7 @@ auto ArmDSP::step(uint clocks) -> void {
|
||||
//a0 ignored
|
||||
|
||||
auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
|
||||
uint8 data = 0x00;
|
||||
addr &= 0xff06;
|
||||
@@ -83,7 +83,7 @@ auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
||||
}
|
||||
|
||||
auto ArmDSP::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
|
||||
addr &= 0xff06;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
//ARMv3 (ARM60)
|
||||
|
||||
struct ArmDSP : Processor::ARM, Cothread {
|
||||
struct ArmDSP : Processor::ARM, Thread {
|
||||
#include "registers.hpp"
|
||||
|
||||
ArmDSP();
|
||||
|
@@ -1,6 +1,3 @@
|
||||
struct Coprocessor : Cothread {
|
||||
};
|
||||
|
||||
#include <sfc/coprocessor/icd2/icd2.hpp>
|
||||
#include <sfc/coprocessor/mcc/mcc.hpp>
|
||||
#include <sfc/coprocessor/nss/nss.hpp>
|
||||
|
@@ -27,7 +27,7 @@ auto EpsonRTC::main() -> void {
|
||||
}
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto EpsonRTC::init() -> void {
|
||||
@@ -136,7 +136,7 @@ auto EpsonRTC::sync() -> void {
|
||||
}
|
||||
|
||||
auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr &= 3;
|
||||
|
||||
if(addr == 0) {
|
||||
@@ -161,7 +161,7 @@ auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
||||
}
|
||||
|
||||
auto EpsonRTC::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr &= 3, data &= 15;
|
||||
|
||||
if(addr == 0) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
//Epson RTC-4513 Real-Time Clock
|
||||
|
||||
struct EpsonRTC : Cothread {
|
||||
struct EpsonRTC : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
|
@@ -25,7 +25,7 @@ auto Event::main() -> void {
|
||||
}
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Event::init() -> void {
|
||||
@@ -47,6 +47,7 @@ auto Event::power() -> void {
|
||||
|
||||
auto Event::reset() -> void {
|
||||
create(Event::Enter, 1);
|
||||
|
||||
for(auto n : range(ram.size())) ram.write(n, 0x00);
|
||||
status = 0x00;
|
||||
select = 0x00;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
//* Campus Challenge '92
|
||||
//* Powerfest '94
|
||||
|
||||
struct Event : Cothread {
|
||||
struct Event : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto init() -> void;
|
||||
|
@@ -15,14 +15,14 @@ auto HitachiDSP::main() -> void {
|
||||
for(auto n : range(mmio.dmaLength)) {
|
||||
write(mmio.dmaTarget + n, read(mmio.dmaSource + n));
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
mmio.dma = false;
|
||||
}
|
||||
|
||||
exec(mmio.programOffset);
|
||||
step(1);
|
||||
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto HitachiDSP::init() -> void {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
struct HitachiDSP : Processor::HG51B, Cothread {
|
||||
struct HitachiDSP : Processor::HG51B, Thread {
|
||||
MappedRAM rom;
|
||||
MappedRAM ram;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user