mirror of https://github.com/bsnes-emu/bsnes.git
Update to v100r06 release.
byuu says: Up to ten 68K instructions out of somewhere between 61 and 88, depending upon which PDF you look at. Of course, some of them aren't 100% completed yet, either. Lots of craziness with MOVEM, and BCC has a BSR variant that needs stack push/pop functions. This WIP actually took over eight hours to make, going through every possible permutation on how to design the core itself. The updated design now builds both the instruction decoder+dispatcher and the disassembler decoder into the same main loop during M68K's constructor. The special cases are also really psychotic on this processor, and I'm afraid of missing something via the fallthrough cases. So instead, I'm ordering the instructions alphabetically, and including exclusion cases to ignore binding invalid cases. If I end up remapping an existing register, then it'll throw a run-time assertion at program startup. I wanted very much to get rid of struct EA (EffectiveAddress), but it's too difficult to keep track of the internal effective address without it. So I split out the size to a separate parameter, since every opcode only has one size parameter, and otherwise it was getting duplicated in opcodes that take two EAs, and was also awkward with the flag testing. It's a bit more typing, but I feel it's more clean this way. Overall, I'm really worried this is going to be too slow. I don't want to turn the EA stuff into templates, because that will massively bloat out compilation times and object sizes, and will also need a special DSL preprocessor since C++ doesn't have a static for loop. I can definitely optimize a lot of EA's address/read/write functions away once the core is completed, but it's never going to hold a candle to a templatized 68K core. ---- Forgot to include the SA-1 regression fix. I always remember immediately after I upload and archive the WIP. Will try to get that in next time, I guess.
This commit is contained in:
parent
b72f35a13e
commit
0d6a09f9f8
|
@ -11,7 +11,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "100.05";
|
||||
static const string Version = "100.06";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -1,160 +1,163 @@
|
|||
auto M68K::_readByte(uint32 addr) -> uint8 {
|
||||
return read(addr);
|
||||
}
|
||||
|
||||
auto M68K::_readWord(uint32 addr) -> uint16 {
|
||||
uint16 data = _readByte(addr + 0) << 8;
|
||||
return data |= _readByte(addr + 1) << 0;
|
||||
}
|
||||
|
||||
auto M68K::_readLong(uint32 addr) -> uint32 {
|
||||
uint32 data = _readWord(addr + 0) << 16;
|
||||
return data |= _readWord(addr + 2) << 0;
|
||||
}
|
||||
|
||||
auto M68K::_readPC(uint size) -> uint32 {
|
||||
uint32 data = _readWord(_pc);
|
||||
_pc += 2;
|
||||
if(size == Byte) return (uint8)data;
|
||||
if(size == Word) return data;
|
||||
data = (data << 16) | _readWord(_pc);
|
||||
_pc += 2;
|
||||
return data;
|
||||
}
|
||||
|
||||
auto M68K::_immediate(uint size) -> string {
|
||||
if(size == Byte) return {"#$", hex(_readPC(Byte), 2L)};
|
||||
if(size == Word) return {"#$", hex(_readPC(Word), 4L)};
|
||||
if(size == Long) return {"#$", hex(_readPC(Long), 8L)};
|
||||
return "#???";
|
||||
}
|
||||
|
||||
auto M68K::_address(uint size, EA& ea) -> string {
|
||||
if(ea.mode == 7) {
|
||||
if(ea.reg == 2) return {"$", hex(_pc + (int16)_readPC(Word), 6L)};
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
auto M68K::_read(uint size, EA& ea) -> string {
|
||||
if(ea.mode == 0) return {"d", ea.reg};
|
||||
if(ea.mode == 1) return {"a", ea.reg};
|
||||
if(ea.mode == 2) return {"(a", ea.reg, ")"};
|
||||
if(ea.mode == 3) return {"(a", ea.reg, ")+"};
|
||||
if(ea.mode == 4) return {"-(a", ea.reg, ")"};
|
||||
if(ea.mode == 5) return {"($", hex(r.a(ea.reg) + (int16)_readPC(Word), 6L), ")"};
|
||||
if(ea.mode == 7) {
|
||||
if(ea.reg == 1) return {"($", hex(_readPC(Long), 6L), ")"};
|
||||
if(ea.reg == 4) {
|
||||
if(size == Byte) return {"#$", hex(_readPC(Byte), 2L)};
|
||||
if(size == Word) return {"#$", hex(_readPC(Word), 4L)};
|
||||
if(size == Long) return {"#$", hex(_readPC(Long), 8L)};
|
||||
}
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
auto M68K::_write(uint size, EA& ea) -> string {
|
||||
return _read(size, ea);
|
||||
}
|
||||
|
||||
auto M68K::_branch(uint displacement) -> string {
|
||||
uint16 word = _readPC();
|
||||
if(displacement) displacement = (int8)displacement, _pc -= 2;
|
||||
else displacement = (int16)displacement;
|
||||
return {"$", hex(_pc + displacement, 6L)};
|
||||
}
|
||||
|
||||
auto M68K::_suffix(uint size) -> string {
|
||||
if(size == Byte) return ".b";
|
||||
if(size == Word) return ".w";
|
||||
if(size == Long) return ".l";
|
||||
return ".?";
|
||||
}
|
||||
|
||||
auto M68K::_condition(uint condition) -> string {
|
||||
static const string conditions[16] = {
|
||||
"ra", "sr", "hi", "ls", "cc", "cs", "ne", "eq",
|
||||
"vc", "vs", "pl", "mi", "ge", "lt", "gt", "le",
|
||||
};
|
||||
return conditions[(uint4)condition];
|
||||
}
|
||||
|
||||
auto M68K::disassemble(uint32 pc) -> string {
|
||||
auto readByte = [&](uint32 addr) -> uint8 {
|
||||
return read(addr);
|
||||
};
|
||||
auto readWord = [&](uint32 addr) -> uint16 {
|
||||
uint16 data = read(addr + 0) << 8;
|
||||
return data |= read(addr + 1) << 0;
|
||||
};
|
||||
auto readLong = [&](uint32 addr) -> uint32 {
|
||||
uint32 data = readWord(addr + 0) << 16;
|
||||
return data |= readWord(addr + 2) << 0;
|
||||
};
|
||||
auto readWordPC = [&]() -> uint16 {
|
||||
auto data = readWord(pc);
|
||||
pc += 2;
|
||||
return data;
|
||||
};
|
||||
auto readLongPC = [&]() -> uint32 {
|
||||
uint32 data = readWordPC() << 16;
|
||||
return data |= readWordPC() << 0;
|
||||
};
|
||||
|
||||
auto size = [&](uint2 size) -> string {
|
||||
if(size == 0) return ".b";
|
||||
if(size == 1) return ".w";
|
||||
if(size == 2) return ".l";
|
||||
return ".?";
|
||||
};
|
||||
|
||||
auto branch = [&](uint8 displacementByte) -> string {
|
||||
uint16 displacementWord = readWordPC();
|
||||
if(displacementByte) pc -= 2;
|
||||
return {"$", hex(pc + (displacementByte ? (int8_t)displacementByte : ((int16_t)displacementWord - 2)), 6L)};
|
||||
};
|
||||
|
||||
auto cc = [&](uint4 condition) -> string {
|
||||
switch(condition) {
|
||||
case 0: return "ra";
|
||||
case 1: return "sr";
|
||||
case 2: return "hi";
|
||||
case 3: return "ls";
|
||||
case 4: return "cc";
|
||||
case 5: return "cs";
|
||||
case 6: return "ne";
|
||||
case 7: return "eq";
|
||||
case 8: return "vc";
|
||||
case 9: return "vs";
|
||||
case 10: return "pl";
|
||||
case 11: return "mi";
|
||||
case 12: return "ge";
|
||||
case 13: return "lt";
|
||||
case 14: return "gt";
|
||||
case 15: return "le";
|
||||
}
|
||||
unreachable;
|
||||
};
|
||||
|
||||
auto ea = [&](uint2 size, uint3 mode, uint3 reg) -> string {
|
||||
if(mode == 3) return {"(a", reg, ")+"};
|
||||
|
||||
if(mode == 7) {
|
||||
if(reg == 0) return {"($", hex((int16)readWordPC(), 6L), ")"};
|
||||
if(reg == 1) return {"($", hex(readLongPC(), 6L), ")"};
|
||||
if(reg == 2) return {"($", hex(pc + (int16)readWordPC(), 6L), ")"};
|
||||
}
|
||||
|
||||
return "???";
|
||||
};
|
||||
|
||||
auto rd = [&](uint3 reg) -> string {
|
||||
static const string name[8] = {"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7"};
|
||||
return name[reg];
|
||||
};
|
||||
|
||||
auto ra = [&](uint3 reg) -> string {
|
||||
static const string name[8] = {"a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp"};
|
||||
return name[reg];
|
||||
};
|
||||
|
||||
string s, name, args;
|
||||
s.append(hex(pc, 6L), " ");
|
||||
|
||||
auto opcode = readWordPC();
|
||||
s.append(hex(opcode, 4L), " ");
|
||||
|
||||
if(0);
|
||||
|
||||
#define match(pattern) else if( \
|
||||
(opcode & std::integral_constant<uint16_t, bit::mask(pattern)>::value) \
|
||||
== std::integral_constant<uint16_t, bit::test(pattern)>::value \
|
||||
)
|
||||
#define bit(n) (opcode >> n & 1)
|
||||
#define bits(hi, lo) ((opcode >> lo) & ((1 << (hi - lo + 1)) - 1))
|
||||
|
||||
//NOP
|
||||
match("0100 1110 0111 0001") {
|
||||
name = {"nop"};
|
||||
}
|
||||
|
||||
//MOVEM
|
||||
match("0100 1-00 1--- ----") {
|
||||
name = {"movem", size(1 + bit(6))};
|
||||
bool direction = bit(10);
|
||||
uint16 list = readWordPC();
|
||||
|
||||
string regs;
|
||||
for(auto n : range(8)) if(list & 1 << (0 + n)) regs.append("d", n, ",");
|
||||
for(auto n : range(8)) if(list & 1 << (8 + n)) regs.append("a", n, ",");
|
||||
regs.trimRight(",");
|
||||
|
||||
if(direction == 0) {
|
||||
args = {regs, ",", ea(1 + bit(6), bits(5,3), bits(2,0))};
|
||||
} else {
|
||||
args = {ea(1 + bit(6), bits(5,3), bits(2,0)), ",", regs};
|
||||
}
|
||||
}
|
||||
|
||||
//TST
|
||||
match("0100 1010 ---- ----") {
|
||||
name = {"tst", size(bits(7,6))};
|
||||
args = {ea(bits(7,6), bits(5,3), bits(2,0))};
|
||||
}
|
||||
|
||||
//LEA
|
||||
match("0100 ---1 11-- ----") {
|
||||
name = {"lea"};
|
||||
args = {ea(Long, bits(5,3), bits(2,0)), ",", ra(bits(11,9))};
|
||||
}
|
||||
|
||||
//BCC
|
||||
match("0110 ---- ---- ----") {
|
||||
name = {"b", cc(bits(11,8))};
|
||||
args = {branch(bits(7,0))};
|
||||
}
|
||||
|
||||
#undef match
|
||||
#undef bit
|
||||
#undef bits
|
||||
|
||||
else {
|
||||
name = {"???"};
|
||||
}
|
||||
|
||||
s.append(name.size(-8), args);
|
||||
return s;
|
||||
uint16 opcode;
|
||||
return {hex(_pc = pc, 6L), " ", hex(opcode = _readPC(), 4L), " ", disassembleTable[opcode]()};
|
||||
}
|
||||
|
||||
auto M68K::disassembleRegisters() -> string {
|
||||
return {
|
||||
hex(r.d0, 8L), " ", hex(r.d1, 8L), " ", hex(r.d2, 8L), " ", hex(r.d3, 8L), " ",
|
||||
hex(r.d4, 8L), " ", hex(r.d5, 8L), " ", hex(r.d6, 8L), " ", hex(r.d7, 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.a0, 8L), " ", hex(r.a1, 8L), " ", hex(r.a2, 8L), " ", hex(r.a3, 8L), " ",
|
||||
hex(r.a4, 8L), " ", hex(r.a5, 8L), " ", hex(r.a6, 8L), " ", hex(r.usp, 8L), " ", hex(r.ssp, 8L)
|
||||
hex(r.a4, 8L), " ", hex(r.a5, 8L), " ", hex(r.a6, 8L), " ", hex(r.ssp, 8L), " ", hex(r.usp, 8L)
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto M68K::disassembleANDI(uint size, EA modify) -> string {
|
||||
return {"andi", _suffix(size), " ", _immediate(size), ",", _read(size, modify)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleBCC(uint condition, uint displacement) -> string {
|
||||
return {"b", _condition(condition), " ", _branch(displacement)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleLEA(uint target, EA source) -> string {
|
||||
return {"lea ", _address(Long, source), ",a", target};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE(uint size, EA target, EA source) -> string {
|
||||
return {"move", _suffix(size), " ", _read(size, source), ",", _write(size, target)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEA(uint size, uint target, EA source) -> string {
|
||||
return {"movea ", _read(size, source), ",a", target};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEM(uint direction, uint size, EA source) -> string {
|
||||
string op{"movem", _suffix(size), " "};
|
||||
|
||||
uint16 list = _readPC();
|
||||
string regs;
|
||||
for(auto n : range(8)) if(list.bit(0 + n)) regs.append("d", n, ",");
|
||||
regs.trimRight(",");
|
||||
if(regs && list >> 8) regs.append("/");
|
||||
for(auto n : range(8)) if(list.bit(8 + n)) regs.append("a", n, ",");
|
||||
regs.trimRight(",");
|
||||
|
||||
if(direction == 0) {
|
||||
return {op, regs, ",", _read(size, source)};
|
||||
} else {
|
||||
return {op, _read(size, source), ",", regs};
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEQ(uint target, uint immediate) -> string {
|
||||
return {"moveq #$", hex(immediate, 2L), ",d", target};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE_USP(uint direction, uint reg) -> string {
|
||||
if(direction == 0) {
|
||||
return {"move a", reg, ",usp"};
|
||||
} else {
|
||||
return {"move usp,a", reg};
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::disassembleNOP() -> string {
|
||||
return {"nop "};
|
||||
}
|
||||
|
||||
auto M68K::disassembleTST(uint size, EA source) -> string {
|
||||
return {"tst", _suffix(size), " ", _read(size, source)};
|
||||
}
|
||||
|
|
|
@ -1,34 +1,72 @@
|
|||
auto M68K::signExtend(uint2 size, uint32 data) -> int32 {
|
||||
if(size == 0) return (int8)data;
|
||||
if(size == 1) return (int16)data;
|
||||
if(size == 2) return (int32)data;
|
||||
auto M68K::sign(uint size, uint32 data) -> int32 {
|
||||
if(size == Byte) return (int8)data;
|
||||
if(size == Word) return (int16)data;
|
||||
if(size == Long) return (int32)data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto M68K::address(EA& ea) -> uint32 {
|
||||
auto M68K::address(uint size, EA& ea) -> uint32 {
|
||||
if(ea.valid) return ea.address;
|
||||
ea.valid = true;
|
||||
|
||||
if(ea.mode == 0) return ea.address = r.d(ea.reg);
|
||||
if(ea.mode == 1) return ea.address = r.a(ea.reg);
|
||||
|
||||
if(ea.mode == 2) return ea.address = r.a(ea.reg);
|
||||
if(ea.mode == 3) return ea.address = r.a(ea.reg);
|
||||
if(ea.mode == 4) return ea.address = r.a(ea.reg);
|
||||
if(ea.mode == 5) return ea.address = r.a(ea.reg) + (int16)readPC(Word);
|
||||
if(ea.mode == 7) {
|
||||
if(ea.reg == 0) return ea.address = (int16)readWordPC();
|
||||
if(ea.reg == 1) return ea.address = readLongPC();
|
||||
if(ea.reg == 2) return ea.address = r.pc, ea.address += (int16)readWordPC();
|
||||
if(ea.reg == 0) return ea.address = (int16)readPC(Word);
|
||||
if(ea.reg == 1) return ea.address = readPC(Long);
|
||||
if(ea.reg == 2) return ea.address = r.pc, ea.address += (int16)readPC(Word);
|
||||
if(ea.reg == 4) {
|
||||
if(size == Byte) return ea.address = readPC(Byte);
|
||||
if(size == Word) return ea.address = readPC(Word);
|
||||
if(size == Long) return ea.address = readPC(Long);
|
||||
}
|
||||
}
|
||||
|
||||
return ea.address = 0;
|
||||
}
|
||||
|
||||
auto M68K::read(EA& ea) -> uint32 {
|
||||
address(ea);
|
||||
if(ea.mode < 2 || (ea.mode == 7 && ea.reg == 4)) return ea.address;
|
||||
return readAbsolute(ea.size, ea.address);
|
||||
auto M68K::read(uint size, EA& ea) -> uint32 {
|
||||
address(size, ea);
|
||||
|
||||
if(ea.mode == 0) return r.d(ea.reg);
|
||||
if(ea.mode == 1) return r.a(ea.reg);
|
||||
if(ea.mode == 2) return read(size, ea.address);
|
||||
if(ea.mode == 3) {
|
||||
auto data = read(size, ea.address);
|
||||
ea.address += size, r.a(ea.reg) += size;
|
||||
return data;
|
||||
}
|
||||
if(ea.mode == 4) {
|
||||
ea.address -= size, r.a(ea.reg) -= size;
|
||||
return read(size, ea.address);
|
||||
}
|
||||
if(ea.mode == 5) return read(size, ea.address);
|
||||
if(ea.mode == 7) {
|
||||
if(ea.reg == 0) return read(size, ea.address);
|
||||
if(ea.reg == 1) return read(size, ea.address);
|
||||
if(ea.reg == 2) return read(size, ea.address);
|
||||
if(ea.reg == 4) return ea.address;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto M68K::write(EA& ea, uint32 data) -> void {
|
||||
address(ea);
|
||||
auto M68K::write(uint size, EA& ea, uint32 data) -> void {
|
||||
address(size, ea);
|
||||
|
||||
if(ea.mode == 0) {
|
||||
r.d(ea.reg) = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(ea.mode == 1) {
|
||||
r.a(ea.reg) = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,50 +10,148 @@ auto M68K::instruction() -> void {
|
|||
instructionsExecuted++;
|
||||
print(disassembleRegisters(), "\n", disassemble(r.pc), "\n\n");
|
||||
|
||||
opcode = readWordPC();
|
||||
opcode = readPC();
|
||||
return instructionTable[opcode]();
|
||||
}
|
||||
|
||||
M68K::M68K() {
|
||||
for(uint opcode : range(65536)) instructionTable[opcode] = [=] { trap(); };
|
||||
|
||||
#define match(pattern) else if( \
|
||||
#define match(pattern) if( \
|
||||
(opcode & std::integral_constant<uint16_t, bit::mask(pattern)>::value) \
|
||||
== std::integral_constant<uint16_t, bit::test(pattern)>::value \
|
||||
) instructionTable[opcode] = [=]
|
||||
#define bit(n) (opcode >> n & 1)
|
||||
#define bits(hi, lo) ((opcode >> lo) & ((1 << (hi - lo + 1)) - 1))
|
||||
)
|
||||
|
||||
for(uint opcode : range(65536)) {
|
||||
if(0);
|
||||
#define bind(name, ...) \
|
||||
instructionTable[opcode] = [=] { return instruction##name(__VA_ARGS__); }; \
|
||||
disassembleTable[opcode] = [=] { return disassemble##name(__VA_ARGS__); }; \
|
||||
|
||||
#define bit(x) (uint)opcode.bit(x)
|
||||
#define bits(x, y) (uint)opcode.bits(x, y)
|
||||
|
||||
for(uint16 opcode : range(65536)) {
|
||||
|
||||
//ANDI
|
||||
match("0000 0010 ---- ----") {
|
||||
auto size = bits(7,6);
|
||||
auto mode = bits(5,3);
|
||||
auto reg = bits(2,0);
|
||||
|
||||
size = size == 0 ? Byte : size == 1 ? Word : size == 2 ? Long : 0;
|
||||
if(size && mode != 1) {
|
||||
bind(ANDI, size, {mode, reg});
|
||||
}
|
||||
}
|
||||
|
||||
//BCC
|
||||
match("0110 ---- ---- ----") {
|
||||
auto condition = bits(11,8);
|
||||
auto displacement = bits(7,0);
|
||||
|
||||
if(true) {
|
||||
bind(BCC, condition, displacement);
|
||||
}
|
||||
}
|
||||
|
||||
//LEA
|
||||
match("0100 ---1 11-- ----") {
|
||||
auto target = bits(11,9);
|
||||
auto mode = bits(5,3);
|
||||
auto reg = bits(2,0);
|
||||
|
||||
if(mode == 2 || mode == 5 || mode == 6 || (mode == 7 && reg <= 4)) {
|
||||
bind(LEA, target, {mode, reg});
|
||||
}
|
||||
}
|
||||
|
||||
//MOVE
|
||||
match("00-- ---- ---- ----") {
|
||||
auto size = bits(13,12);
|
||||
auto targetReg = bits(11,9);
|
||||
auto targetMode = bits(8,6);
|
||||
auto sourceMode = bits(5,3);
|
||||
auto sourceReg = bits(2,0);
|
||||
|
||||
size = size == 1 ? Byte : size == 3 ? Word : size == 2 ? Long : 0;
|
||||
if(size && targetMode != 1) {
|
||||
bind(MOVE, size, {targetMode, targetReg}, {sourceMode, sourceReg});
|
||||
}
|
||||
}
|
||||
|
||||
//MOVEA
|
||||
match("00-- ---0 01-- ----") {
|
||||
auto size = bits(13,12);
|
||||
auto target = bits(11,9);
|
||||
auto sourceMode = bits(5,3);
|
||||
auto sourceReg = bits(2,0);
|
||||
|
||||
size = size == 3 ? Word : size == 2 ? Long : 0;
|
||||
if(size) {
|
||||
bind(MOVEA, size, target, {sourceMode, sourceReg});
|
||||
}
|
||||
}
|
||||
|
||||
//MOVEM
|
||||
match("0100 1-00 1--- ----") {
|
||||
auto direction = bit(10);
|
||||
auto size = bit(6);
|
||||
auto mode = bits(5,3);
|
||||
auto reg = bits(2,0);
|
||||
|
||||
size = size == 0 ? Word : size == 1 ? Long : 0;
|
||||
if((direction == 0 && (mode == 2 || mode == 4 || mode == 5 || mode == 6 || (mode == 7 && reg <= 3)))
|
||||
|| (direction == 1 && (mode == 2 || mode == 3 || mode == 5 || mode == 6 || (mode == 7 && reg <= 3)))) {
|
||||
bind(MOVEM, direction, size, {mode, reg});
|
||||
}
|
||||
}
|
||||
|
||||
//MOVEQ
|
||||
match("0111 ---0 ---- ----") {
|
||||
auto target = bits(11,9);
|
||||
auto immediate = bits(7,0);
|
||||
|
||||
if(true) {
|
||||
bind(MOVEQ, target, immediate);
|
||||
}
|
||||
}
|
||||
|
||||
//MOVE_USP
|
||||
match("0100 1110 0110 ----") {
|
||||
auto direction = bit(3);
|
||||
auto reg = bits(2,0);
|
||||
|
||||
if(true) {
|
||||
bind(MOVE_USP, direction, reg);
|
||||
}
|
||||
}
|
||||
|
||||
//NOP
|
||||
match("0100 1110 0111 0001") {
|
||||
instructionNOP();
|
||||
};
|
||||
if(true) {
|
||||
bind(NOP);
|
||||
}
|
||||
}
|
||||
|
||||
//MOVEM (direction, size, mode, register)
|
||||
match("0100 1-00 1--- ----") {
|
||||
instructionMOVEM(bit(10), EA{1 + bit(6), bits(5,3), bits(2,0)});
|
||||
};
|
||||
|
||||
//TST (size, mode, register)
|
||||
//TST
|
||||
match("0100 1010 ---- ----") {
|
||||
instructionTST(EA{bits(7,6), bits(5,3), bits(2,0)});
|
||||
};
|
||||
auto size = bits(7,6);
|
||||
auto mode = bits(5,3);
|
||||
auto reg = bits(2,0);
|
||||
|
||||
//LEA (An, mode, register)
|
||||
match("0100 ---1 11-- ----") {
|
||||
instructionLEA(bits(11,9), EA{Long, bits(5,3), bits(2,0)});
|
||||
};
|
||||
size = size == 0 ? Byte : size == 1 ? Word : size == 2 ? Long : 0;
|
||||
if(size) {
|
||||
bind(TST, size, {mode, reg});
|
||||
}
|
||||
}
|
||||
|
||||
//BCC (condition, displacement)
|
||||
match("0110 ---- ---- ----") {
|
||||
instructionBCC(bits(11,8), bits(7,0));
|
||||
};
|
||||
}
|
||||
|
||||
for(uint16 opcode : range(65536)) {
|
||||
if(instructionTable[opcode]) continue;
|
||||
instructionTable[opcode] = [=] { trap(); };
|
||||
disassembleTable[opcode] = [=] { return string{"???"}; };
|
||||
}
|
||||
|
||||
#undef match
|
||||
#undef bind
|
||||
#undef bit
|
||||
#undef bits
|
||||
}
|
||||
|
|
|
@ -22,34 +22,83 @@ auto M68K::testCondition(uint4 condition) -> bool {
|
|||
|
||||
//
|
||||
|
||||
auto M68K::instructionBCC(uint4 condition, uint8 displacementByte) -> void {
|
||||
uint16 displacementWord = readWordPC();
|
||||
if(displacementByte) r.pc -= 2;
|
||||
auto M68K::instructionANDI(uint size, EA modify) -> void {
|
||||
auto data = readPC(size);
|
||||
write(size, modify, data = read(size, modify) & data);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = data == 0;
|
||||
r.n = sign(size, data) < 0;
|
||||
}
|
||||
|
||||
auto M68K::instructionBCC(uint condition, uint displacement) -> void {
|
||||
auto word = readPC();
|
||||
if(displacement) displacement = (int8)displacement, r.pc -= 2;
|
||||
else displacement = (int16)word;
|
||||
if(condition == 1) {
|
||||
condition = 0;
|
||||
//pushLong(r.pc);
|
||||
}
|
||||
if(testCondition(condition)) {
|
||||
r.pc += displacementByte ? (int8_t)displacementByte : ((int16_t)displacementWord - 2);
|
||||
if(testCondition(condition)) r.pc += displacement;
|
||||
}
|
||||
|
||||
auto M68K::instructionLEA(uint target, EA source) -> void {
|
||||
r.a(target) = address(Long, source);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE(uint size, EA target, EA source) -> void {
|
||||
auto data = read(size, source);
|
||||
write(size, target, data);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = data == 0;
|
||||
r.n = sign(size, data) < 0;
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEA(uint size, uint target, EA source) -> void {
|
||||
r.d(target) = read(size, source);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEM(uint direction, uint size, EA source) -> void {
|
||||
auto list = readPC();
|
||||
|
||||
for(uint n : range(8)) {
|
||||
if(list.bit(0 + n)) r.d(n) = read(size, source);
|
||||
}
|
||||
|
||||
for(uint n : range(8)) {
|
||||
if(list.bit(8 + n)) r.a(n) = read(size, source);
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::instructionLEA(uint3 wr, EA ea) -> void {
|
||||
r.a(wr) = address(ea);
|
||||
auto M68K::instructionMOVEQ(uint target, uint immediate) -> void {
|
||||
r.d(target) = immediate;
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = immediate == 0;
|
||||
r.v = sign(Byte, immediate) < 0;
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEM(uint1 direction, EA ea) -> void {
|
||||
auto list = readWordPC();
|
||||
auto M68K::instructionMOVE_USP(uint direction, uint reg) -> void {
|
||||
if(!r.s) trap();
|
||||
if(direction == 0) {
|
||||
r.usp = r.a(reg);
|
||||
} else {
|
||||
r.a(reg) = r.usp;
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::instructionNOP() -> void {
|
||||
}
|
||||
|
||||
auto M68K::instructionTST(EA ea) -> void {
|
||||
auto data = read(ea);
|
||||
auto M68K::instructionTST(uint size, EA source) -> void {
|
||||
auto data = read(size, source);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = data == 0;
|
||||
r.n = signExtend(ea.size, data) < 0;
|
||||
r.n = sign(size, data) < 0;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
namespace Processor {
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "ea.cpp"
|
||||
#include "instruction.cpp"
|
||||
|
@ -15,40 +16,12 @@ auto M68K::power() -> void {
|
|||
auto M68K::reset() -> void {
|
||||
instructionsExecuted = 0;
|
||||
|
||||
r.d0 = r.d1 = r.d2 = r.d3 = r.d4 = r.d5 = r.d6 = r.d7 = 0;
|
||||
r.a0 = r.a1 = r.a2 = r.a3 = r.a4 = r.a5 = r.a6 = r.usp = r.ssp = 0;
|
||||
for(uint n : range(8)) r.d(n) = 0;
|
||||
for(uint n : range(7)) r.a(n) = 0;
|
||||
r.ssp = 0;
|
||||
r.usp = 0;
|
||||
r.pc = 0;
|
||||
r.ccr = 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto M68K::Registers::d(uint3 reg) -> uint32& {
|
||||
switch(reg) {
|
||||
case 0: return d0;
|
||||
case 1: return d1;
|
||||
case 2: return d2;
|
||||
case 3: return d3;
|
||||
case 4: return d4;
|
||||
case 5: return d5;
|
||||
case 6: return d6;
|
||||
case 7: return d7;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto M68K::Registers::a(uint3 reg) -> uint32& {
|
||||
switch(reg) {
|
||||
case 0: return a0;
|
||||
case 1: return a1;
|
||||
case 2: return a2;
|
||||
case 3: return a3;
|
||||
case 4: return a4;
|
||||
case 5: return a5;
|
||||
case 6: return a6;
|
||||
case 7: return ssp;
|
||||
}
|
||||
unreachable;
|
||||
r.sr = 0x2000;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
namespace Processor {
|
||||
|
||||
struct M68K {
|
||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||
|
||||
M68K();
|
||||
|
||||
virtual auto step(uint clocks) -> void = 0;
|
||||
|
@ -19,28 +21,23 @@ struct M68K {
|
|||
auto readWord(uint32 addr) -> uint16;
|
||||
auto readLong(uint32 addr) -> uint32;
|
||||
|
||||
auto readWordPC() -> uint16;
|
||||
auto readLongPC() -> uint32;
|
||||
|
||||
auto readAbsolute(uint2 size, uint32 addr) -> uint32;
|
||||
auto read(uint size, uint32 addr) -> uint32;
|
||||
auto readPC(uint size = Word) -> uint32;
|
||||
|
||||
//ea.cpp
|
||||
struct EA {
|
||||
EA(uint2 size, uint3 mode, uint3 reg) : size(size), mode(mode), reg(reg) {}
|
||||
|
||||
uint2 size;
|
||||
uint3 mode;
|
||||
uint3 reg;
|
||||
uint mode;
|
||||
uint reg;
|
||||
|
||||
boolean valid;
|
||||
uint32 address;
|
||||
};
|
||||
|
||||
auto signExtend(uint2 size, uint32 data) -> int32;
|
||||
auto sign(uint size, uint32 data) -> int32;
|
||||
|
||||
auto address(EA& ea) -> uint32;
|
||||
auto read(EA& ea) -> uint32;
|
||||
auto write(EA& ea, uint32 data) -> void;
|
||||
auto address(uint size, EA& ea) -> uint32;
|
||||
auto read(uint size, EA& ea) -> uint32;
|
||||
auto write(uint size, EA& ea, uint32 data) -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto trap() -> void;
|
||||
|
@ -49,42 +46,76 @@ struct M68K {
|
|||
//instructions.cpp
|
||||
auto testCondition(uint4 condition) -> bool;
|
||||
|
||||
auto instructionBCC(uint4 condition, uint8 displacementByte) -> void;
|
||||
auto instructionLEA(uint3 wr, EA ea) -> void;
|
||||
auto instructionMOVEM(uint1 direction, EA ea) -> void;
|
||||
auto instructionANDI(uint size, EA modify) -> void;
|
||||
auto instructionBCC(uint condition, uint displacement) -> void;
|
||||
auto instructionLEA(uint target, EA source) -> void;
|
||||
auto instructionMOVE(uint size, EA target, EA source) -> void;
|
||||
auto instructionMOVEA(uint size, uint target, EA source) -> void;
|
||||
auto instructionMOVEM(uint direction, uint size, EA source) -> void;
|
||||
auto instructionMOVEQ(uint target, uint immediate) -> void;
|
||||
auto instructionMOVE_USP(uint direction, uint reg) -> void;
|
||||
auto instructionNOP() -> void;
|
||||
auto instructionTST(EA ea) -> void;
|
||||
auto instructionTST(uint size, EA source) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(uint32 pc) -> string;
|
||||
auto disassembleRegisters() -> string;
|
||||
|
||||
enum : uint { Byte = 0, Word = 1, Long = 2 };
|
||||
|
||||
struct Registers {
|
||||
auto d(uint3 reg) -> uint32&;
|
||||
auto a(uint3 reg) -> uint32&;
|
||||
auto d(uint3 r) -> uint32&;
|
||||
auto a(uint3 r) -> uint32&;
|
||||
|
||||
uint32 d0, d1, d2, d3, d4, d5, d6, d7;
|
||||
uint32 a0, a1, a2, a3, a4, a5, a6, usp, ssp;
|
||||
uint32 a0, a1, a2, a3, a4, a5, a6, ssp, usp;
|
||||
uint32 pc;
|
||||
|
||||
union {
|
||||
uint8 ccr;
|
||||
BooleanBitField<uint8_t, 0> c; //carry
|
||||
BooleanBitField<uint8_t, 1> v; //overflow
|
||||
BooleanBitField<uint8_t, 2> z; //zero
|
||||
BooleanBitField<uint8_t, 3> n; //negative
|
||||
BooleanBitField<uint8_t, 4> x; //extend
|
||||
uint16 sr;
|
||||
BooleanBitField<uint16_t, 0> c; //carry
|
||||
BooleanBitField<uint16_t, 1> v; //overflow
|
||||
BooleanBitField<uint16_t, 2> z; //zero
|
||||
BooleanBitField<uint16_t, 3> n; //negative
|
||||
BooleanBitField<uint16_t, 4> x; //extend
|
||||
NaturalBitField<uint16_t,8,10> i; //interrupt mask
|
||||
BooleanBitField<uint16_t, 13> s; //supervisor mode
|
||||
BooleanBitField<uint16_t, 15> t; //trace mode
|
||||
};
|
||||
|
||||
Registers() : ccr(0) {}
|
||||
Registers() : sr(0) {}
|
||||
} r;
|
||||
|
||||
uint16 opcode = 0;
|
||||
uint instructionsExecuted = 0;
|
||||
|
||||
function<void ()> instructionTable[65536];
|
||||
|
||||
private:
|
||||
//disassembler.cpp
|
||||
auto disassembleANDI(uint size, EA modify) -> string;
|
||||
auto disassembleBCC(uint condition, uint displacement) -> string;
|
||||
auto disassembleLEA(uint target, EA source) -> string;
|
||||
auto disassembleMOVE(uint size, EA target, EA source) -> string;
|
||||
auto disassembleMOVEA(uint size, uint target, EA source) -> string;
|
||||
auto disassembleMOVEM(uint direction, uint size, EA source) -> string;
|
||||
auto disassembleMOVEQ(uint target, uint immediate) -> string;
|
||||
auto disassembleMOVE_USP(uint direction, uint reg) -> string;
|
||||
auto disassembleNOP() -> string;
|
||||
auto disassembleTST(uint size, EA source) -> string;
|
||||
|
||||
auto _readByte(uint32 addr) -> uint8;
|
||||
auto _readWord(uint32 addr) -> uint16;
|
||||
auto _readLong(uint32 addr) -> uint32;
|
||||
auto _readPC(uint size = Word) -> uint32;
|
||||
auto _immediate(uint size) -> string;
|
||||
auto _address(uint size, EA& ea) -> string;
|
||||
auto _read(uint size, EA& ea) -> string;
|
||||
auto _write(uint size, EA& ea) -> string;
|
||||
auto _branch(uint displacement) -> string;
|
||||
auto _suffix(uint size) -> string;
|
||||
auto _condition(uint condition) -> string;
|
||||
|
||||
uint32 _pc;
|
||||
function<string ()> disassembleTable[65536];
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -16,22 +16,19 @@ auto M68K::readLong(uint32 addr) -> uint32 {
|
|||
|
||||
//
|
||||
|
||||
auto M68K::readWordPC() -> uint16 {
|
||||
uint16 data = readWord(r.pc);
|
||||
auto M68K::read(uint size, uint32 addr) -> uint32 {
|
||||
if(size == Byte) return readByte(addr);
|
||||
if(size == Word) return readWord(addr);
|
||||
if(size == Long) return readLong(addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto M68K::readPC(uint size) -> uint32 {
|
||||
uint32 data = readWord(r.pc);
|
||||
r.pc += 2;
|
||||
if(size == Byte) return (uint8)data;
|
||||
if(size == Word) return data;
|
||||
data = data << 16 | readWord(r.pc);
|
||||
r.pc += 2;
|
||||
return data;
|
||||
}
|
||||
|
||||
auto M68K::readLongPC() -> uint32 {
|
||||
uint32 data = readWordPC() << 16;
|
||||
return data |= readWordPC() << 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto M68K::readAbsolute(uint2 size, uint32 addr) -> uint32 {
|
||||
if(size == 0) return readByte(addr);
|
||||
if(size == 1) return readWord(addr);
|
||||
if(size == 2) return readLong(addr);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
auto M68K::Registers::d(uint3 r) -> uint32& {
|
||||
switch(r) {
|
||||
case 0: return d0;
|
||||
case 1: return d1;
|
||||
case 2: return d2;
|
||||
case 3: return d3;
|
||||
case 4: return d4;
|
||||
case 5: return d5;
|
||||
case 6: return d6;
|
||||
case 7: return d7;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto M68K::Registers::a(uint3 r) -> uint32& {
|
||||
switch(r) {
|
||||
case 0: return a0;
|
||||
case 1: return a1;
|
||||
case 2: return a2;
|
||||
case 3: return a3;
|
||||
case 4: return a4;
|
||||
case 5: return a5;
|
||||
case 6: return a6;
|
||||
case 7: return s ? ssp : usp;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue