Update to v100r08 release.

byuu says:

Six and a half hours this time ... one new opcode, and all old opcodes
now in a deprecated format. Hooray, progress!

For building the table, I've decided to move from:

    for(uint opcode : range(65536)) {
      if(match(...)) bind(opNAME, ...);
    }

To instead having separate for loops for each supported opcode. This
lets me specialize parts I want with templates.

And to this aim, I'm moving to replace all of the
(read,write)(size, ...) functions with (read,write)<Size>(...) functions.

This will amount to the ~70ish instructions being triplicated ot ~210ish
instructions; but I think this is really important.

When I was getting into flag calculations, a ton of conditionals
were needed to mask sizes to byte/word/long. There was also lots of
conditionals in all the memory access handlers.

The template code is ugly, but we eliminate a huge amount of branch
conditions this way.
This commit is contained in:
Tim Allen 2016-07-18 08:11:29 +10:00
parent 059347e575
commit 92fe5b0813
9 changed files with 644 additions and 216 deletions

View File

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

View File

@ -11,7 +11,7 @@ auto M68K::_readLong(uint32 addr) -> uint32 {
return data |= _readWord(addr + 2) << 0;
}
auto M68K::_readPC(uint size) -> uint32 {
auto M68K::_readPC(uint2 size) -> uint32 {
uint32 data = _readWord(_pc);
_pc += 2;
if(size == Byte) return (uint8)data;
@ -21,21 +21,29 @@ auto M68K::_readPC(uint size) -> uint32 {
return data;
}
auto M68K::_immediate(uint size) -> string {
auto M68K::_immediate(uint2 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(uint2 size, uint3 mode, uint3 reg) -> string {
auto M68K::_address(uint8 ea) -> string {
uint3 mode = ea >> 5;
uint3 reg = ea >> 2;
uint2 size = ea >> 0;
if(mode == 7) {
if(reg == 2) return {"$", hex(_pc + (int16)_readPC(Word), 6L)};
}
return "???";
}
auto M68K::_read(uint2 size, uint3 mode, uint3 reg) -> string {
auto M68K::_read(uint8 ea) -> string {
uint3 mode = ea >> 5;
uint3 reg = ea >> 2;
uint2 size = ea >> 0;
if(mode == 0) return {"d", reg};
if(mode == 1) return {"a", reg};
if(mode == 2) return {"(a", reg, ")"};
@ -53,8 +61,8 @@ auto M68K::_read(uint2 size, uint3 mode, uint3 reg) -> string {
return "???";
}
auto M68K::_write(uint2 size, uint3 mode, uint3 reg) -> string {
return _read(size, mode, reg);
auto M68K::_write(uint8 ea) -> string {
return _read(ea);
}
auto M68K::_branch(uint8 displacement) -> string {
@ -64,7 +72,7 @@ auto M68K::_branch(uint8 displacement) -> string {
return {"$", hex(_pc + displacement, 6L)};
}
auto M68K::_suffix(uint size) -> string {
auto M68K::_suffix(uint2 size) -> string {
if(size == Byte) return ".b";
if(size == Word) return ".w";
if(size == Long) return ".l";
@ -103,28 +111,39 @@ auto M68K::disassembleRegisters() -> string {
//
auto M68K::disassembleANDI(uint2 size, uint3 mode, uint3 reg) -> string {
return {"andi", _suffix(size), " ", _immediate(size), ",", _read(size, mode, reg)};
template<uint Size> auto M68K::disassembleADD(uint3 reg, uint1 direction, EA ea) -> string {
string op{"add", _suffix(ea.reg), " "};
return op;
if(direction == 0) {
// return {op, _read(ea), ",d", reg};
} else {
// return {op, "d", reg, ",", _read(ea)};
}
}
auto M68K::disassembleANDI(uint8 ea) -> string {
return {"andi", _suffix(ea), " ", _immediate(ea), ",", _read(ea)};
}
auto M68K::disassembleBCC(uint4 condition, uint8 displacement) -> string {
return {"b", _condition(condition), " ", _branch(displacement)};
}
auto M68K::disassembleLEA(uint3 target, uint3 mode, uint3 reg) -> string {
return {"lea ", _address(Long, mode, reg), ",a", target};
auto M68K::disassembleLEA(uint3 to, uint8 ea) -> string {
return {"lea ", _address(ea), ",a", to};
}
auto M68K::disassembleMOVE(uint2 size, uint3 targetReg, uint3 targetMode, uint3 sourceMode, uint3 sourceReg) -> string {
return {"move", _suffix(size), " ", _read(size, sourceMode, sourceReg), ",", _write(size, targetMode, targetReg)};
auto M68K::disassembleMOVE(uint8 to, uint8 from) -> string {
return {"move", _suffix(from), " ", _read(from), ",", _write(to)};
}
auto M68K::disassembleMOVEA(uint2 size, uint3 target, uint3 mode, uint3 reg) -> string {
return {"movea ", _read(size, mode, reg), ",a", target};
auto M68K::disassembleMOVEA(uint3 to, uint8 from) -> string {
return {"movea ", _read(from), ",a", to};
}
auto M68K::disassembleMOVEM(uint1 direction, uint2 size, uint3 mode, uint3 reg) -> string {
string op{"movem", _suffix(size), " "};
auto M68K::disassembleMOVEM(uint1 direction, uint8 ea) -> string {
string op{"movem", _suffix(ea), " "};
uint16 list = _readPC();
string regs;
@ -135,9 +154,9 @@ auto M68K::disassembleMOVEM(uint1 direction, uint2 size, uint3 mode, uint3 reg)
regs.trimRight(",");
if(direction == 0) {
return {op, regs, ",", _read(size, mode, reg)};
return {op, regs, ",", _read(ea)};
} else {
return {op, _read(size, mode, reg), ",", regs};
return {op, _read(ea), ",", regs};
}
}
@ -157,6 +176,6 @@ auto M68K::disassembleNOP() -> string {
return {"nop "};
}
auto M68K::disassembleTST(uint2 size, uint3 mode, uint3 reg) -> string {
return {"tst", _suffix(size), " ", _read(size, mode, reg)};
auto M68K::disassembleTST(uint8 ea) -> string {
return {"tst", _suffix(ea), " ", _read(ea)};
}

View File

@ -1,137 +1,423 @@
M68K::EA::EA(M68K* self, uint2 size, uint3 mode, uint3 reg) : self(self), size(size), mode(mode), reg(reg) {
if(this->mode == 7) this->mode += this->reg; //speed hack: convert cases {7; 0-4} to {8-12} for switch jump table
address = fetch();
}
//effective addressing
M68K::EA::~EA() {
flush();
}
//encoding:
// d7-d5: mode
// d4-d2: register
// d1-d0: size (0 = byte, 1 = word, 2 = long)
auto M68K::EA::pc() -> uint32& { return self->r.pc; }
auto M68K::EA::d(uint3 reg) -> uint32& { return self->r.d(reg); }
auto M68K::EA::a(uint3 reg) -> uint32& { return self->r.a(reg); }
auto M68K::EA::readPC(uint2 size) -> uint32 { return self->readPC(size); }
auto M68K::EA::read(uint32 addr) -> uint32 { return self->readAbsolute(size, addr); }
auto M68K::EA::write(uint32 addr, uint32 data) -> void { return self->writeAbsolute(size, addr, data); }
auto M68K::EA::fetch() -> uint32 {
switch(mode) {
auto M68K::address(uint8 ea) -> uint32 {
switch(ea) {
//data register direct
case 0: return d(reg);
case 0b000'000'00 ... 0b000'111'11:
return r.d(ea >> 2);
//address register direct
case 1: return a(reg);
case 0b001'000'00 ... 0b001'111'11:
return r.a(ea >> 2);
//address register indirect
case 2: return a(reg);
case 0b010'000'00 ... 0b010'111'11:
return r.a(ea >> 2);
//address register indirect with post-increment
case 3: return a(reg);
case 0b011'000'00 ... 0b011'111'11:
return r.a(ea >> 2);
//address register indirect with pre-decrement
case 4: return a(reg);
case 0b100'000'00 ... 0b100'111'11:
return r.a(ea >> 2);
//address register with displacement
case 5: return a(reg) + (int16)readPC(Word);
//address register indirect with displacement
case 0b101'000'00 ... 0b101'111'11:
return r.a(ea >> 2) + (int16)readPC();
//address register with index
case 6: {
auto word = readPC(Word);
auto index = word & 0x8000 ? a(word >> 12) : d(word >> 12);
//address register indirect with index
case 0b110'000'00 ... 0b110'111'11: {
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
return a(reg) + index + (int8)word;
return r.a(ea >> 2) + index + (int8)word;
}
//absolute short
case 7: return (int16)readPC(Word);
//absolute short indirect
case 0b111'000'00 ... 0b111'000'11:
return (int16)readPC();
//absolute long
case 8: return readPC(Long);
//program counter with displacement
case 9: {
auto base = pc();
return base + (int16)readPC(Word);
//absolute long indirect
case 0b111'001'00 ... 0b111'001'11: {
uint32 address = readPC() << 16;
return address | readPC() << 0;
}
//program counter with index
case 10: {
auto base = pc();
auto word = readPC(Word);
auto index = word & 0x8000 ? a(word >> 12) : d(word >> 12);
//program counter indirect with displacement
case 0b111'010'00 ... 0b111'010'11: {
auto base = r.pc;
return base + (int16)readPC();
}
//program counter indirect with index
case 0b111'011'00 ... 0b111'011'11: {
auto base = r.pc;
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
return base + index + (int8)word;
}
//immediate
case 11: {
if(size == Byte) return (uint8)readPC(Word);
if(size == Word) return readPC(Word);
if(size == Long) return readPC(Long);
//immediate byte
case 0b111'100'00:
return (uint8)readPC();
//immediate word
case 0b111'100'01:
return readPC();
//immediate long
case 0b111'100'10: {
uint32 address = readPC() << 16;
return address | readPC() << 0;
}
}
//invalid
default:
return 0;
return 0;
}
}
auto M68K::EA::read() -> uint32 {
switch(mode) {
case 0: return address;
case 1: return address;
case 2: return read(address);
case 3: {
auto data = read(address);
address += size == Long ? 4 : 2;
template<uint Size> auto M68K::read(EA ea) -> uint32 {
switch(ea.mode) {
case 0: { //data register direct
return clip<Size>(r.d(ea.reg));
}
case 1: { //address register direct
return clip<Size>(r.a(ea.reg));
}
case 2: { //address register indirect
auto& address = r.a(ea.reg);
return read<Size>(address);
}
case 3: { //address register indirect with post-increment
auto& address = r.a(ea.reg);
auto data = read<Size>(address);
address += Size == Long ? 4 : 2;
return data;
}
case 4: {
address -= size == Long ? 4 : 2;
return read(address);
}
case 5: return read(address);
case 6: return read(address);
case 7: return read(address);
case 8: return read(address);
case 9: return read(address);
case 10: return read(address);
case 11: return address;
case 4: { //address register indirect with pre-decrement
auto& address = r.a(ea.reg);
address -= Size == Long ? 4 : 2;
return read<Size>(address);
}
return 0;
}
}
auto M68K::EA::write(uint32 data) -> void {
switch(mode) {
case 0: address = data; return;
case 1: address = data; return;
case 2: return write(address, data);
case 3: {
write(address, data);
address += size == Long ? 4 : 2;
template<uint Size> auto M68K::write(EA ea, uint32 data) -> void {
switch(ea.mode) {
case 0: { //data register direct
r.d(ea.reg) = data;
return;
}
case 4: {
address -= size == Long ? 4 : 2;
return write(address, data);
case 1: { //address register direct
r.a(ea.reg) = data;
return;
}
case 5: return write(address, data);
case 6: return write(address, data);
case 7: return write(address, data);
case 8: return write(address, data);
case 9: return write(address, data);
case 10: return write(address, data);
case 11: address = data; return;
}
}
auto M68K::EA::flush() -> void {
auto M68K::read(uint8 ea) -> uint32 {
switch(ea) {
//data register direct
case 0b000'000'00 ... 0b000'111'11:
return r.d(ea >> 2);
//address register direct
case 0b001'000'00 ... 0b001'111'11:
return r.a(ea >> 2);
//address register indirect
case 0b010'000'00 ... 0b010'111'11: {
auto address = r.a(ea >> 2);
return readAbsolute(ea, address);
}
//address register indirect with post-increment
if(mode == 3) a(reg) = address;
case 0b011'000'00 ... 0b011'111'11: {
auto& address = r.a(ea >> 2);
auto data = readAbsolute(ea, address);
address += 2 + (ea & 2);
return data;
}
//address register indirect with pre-decrement
if(mode == 4) a(reg) = address;
case 0b100'000'00 ... 0b100'111'11: {
auto& address = r.a(ea >> 2);
address -= 2 + (ea & 2);
return readAbsolute(ea, address);
}
mode = 15;
//address register indirect with displacement
case 0b101'000'00 ... 0b101'111'11:
return readAbsolute(ea, r.a(ea >> 2) + (int16)readPC());
//address register indirect with index
case 0b110'000'00 ... 0b110'111'11: {
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
return readAbsolute(ea, r.a(ea >> 2) + index + (int8)word);
}
//absolute short indirect
case 0b111'000'00 ... 0b111'000'11:
return readAbsolute(ea, (int16)readPC());
//absolute long indirect
case 0b111'001'00 ... 0b111'001'11: {
uint32 address = readPC() << 16;
return readAbsolute(ea, address | readPC());
}
//program counter indirect with displacement
case 0b111'010'00 ... 0b111'010'11: {
auto base = r.pc;
return readAbsolute(ea, base + (int16)readPC());
}
//program counter indirect with index
case 0b111'011'00 ... 0b111'011'11: {
auto base = r.pc;
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
return readAbsolute(ea, base + index + (int8)word);
}
//immediate byte
case 0b111'100'00:
return (uint8)readPC();
//immediate word
case 0b111'100'01:
return readPC();
//immediate long
case 0b111'100'10: {
uint32 address = readPC() << 16;
return address | readPC() << 0;
}
//invalid
default:
return 0;
}
}
auto M68K::write(uint8 ea, uint32 data) -> void {
switch(ea) {
//data register direct
case 0b000'000'00 ... 0b000'111'11:
r.d(ea >> 2) = data;
return;
//address register direct
case 0b001'000'00 ... 0b001'111'11:
r.a(ea >> 2) = data;
return;
//address register indirect
case 0b010'000'00 ... 0b010'111'11: {
auto address = r.a(ea >> 2);
return writeAbsolute(ea, address, data);
}
//address register indirect with post-increment
case 0b011'000'00 ... 0b011'111'11: {
auto& address = r.a(ea >> 2);
writeAbsolute(ea, address, data);
address += 2 + (ea & 2);
return;
}
//address register indirect with pre-decrement
case 0b100'000'00 ... 0b100'111'11: {
auto& address = r.a(ea >> 2);
address -= 2 + (ea & 2);
return writeAbsolute(ea, address, data);
}
//address register indirect with displacement
case 0b101'000'00 ... 0b101'111'11:
return writeAbsolute(ea, r.a(ea >> 2) + (int16)readPC(), data);
//address register indirect with index
case 0b110'000'00 ... 0b110'111'11: {
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
return writeAbsolute(ea, r.a(ea >> 2) + index + (int8)word, data);
}
//absolute short indirect
case 0b111'000'00 ... 0b111'000'11:
return writeAbsolute(ea, (int16)readPC(), data);
//absolute long indirect
case 0b111'001'00 ... 0b111'001'11: {
uint32 address = readPC() << 16;
return writeAbsolute(ea, address | readPC(), data);
}
//program counter indirect with displacement
case 0b111'010'00 ... 0b111'010'11: {
auto base = r.pc;
return writeAbsolute(ea, base + (int16)readPC(), data);
}
//program counter indirect with index
case 0b111'011'00 ... 0b111'011'11: {
auto base = r.pc;
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
return writeAbsolute(ea, base + index + (int8)word, data);
}
}
}
auto M68K::modify(uint8 ea, uint32 data, const function<uint32 (uint32, uint32)>& op) -> uint32 {
switch(ea) {
//data register direct
case 0b000'000'00 ... 0b000'111'11: {
auto& address = r.d(ea >> 2);
return address = op(address, data);
}
//address register direct
case 0b001'000'00 ... 0b001'111'11: {
auto& address = r.a(ea >> 2);
return address = op(address, data);
}
//address register indirect
case 0b010'000'00 ... 0b010'111'11: {
auto address = r.a(ea >> 2);
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//address register indirect with post-increment
case 0b011'000'00 ... 0b011'111'11: {
auto& address = r.a(ea >> 2);
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
address += 2 + (ea & 2);
return memory;
}
//address register indirect with pre-decrement
case 0b100'000'00 ... 0b100'111'11: {
auto& address = r.a(ea >> 2);
address -= 2 + (ea & 2);
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//address register indirect with displacement
case 0b101'000'00 ... 0b101'111'11: {
auto address = r.a(ea >> 2) + (int16)readPC();
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//address register indirect with index
case 0b110'000'00 ... 0b110'111'11: {
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x800) index = (int16)index;
auto address = r.a(ea >> 2) + index + (int8)word;
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//absolute short indirect
case 0b111'000'00 ... 0b111'000'11: {
auto address = (int16)readPC();
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//absolute long indirect
case 0b111'001'00 ... 0b111'001'11: {
auto word = readPC();
uint32 address = word << 16 | readPC();
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//program counter indirect with displacement
case 0b111'010'00 ... 0b111'010'11: {
auto address = r.pc;
address += (int16)readPC();
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//program counter indirect with index
case 0b111'011'00 ... 0b111'011'11: {
auto address = r.pc;
auto word = readPC();
auto index = word & 0x8000 ? r.a(word >> 12) : r.d(word >> 12);
if(word & 0x8000) index = (int16)index;
address += index + (int8)word;
auto memory = readAbsolute(ea, address);
writeAbsolute(ea, address, memory = op(memory, data));
return memory;
}
//immediate byte
case 0b111'100'00:
return op((uint8)readPC(), data);
//immediate word
case 0b111'100'01:
return op(readPC(), data);
//immediate long
case 0b111'100'10: {
uint32 immediate = readPC() << 16;
immediate |= readPC();
return op(immediate, data);
}
}
}
auto M68K::flush(uint8 ea, uint32 address) -> void {
//address register indirect with post-increment
//address register indirect with pre-decrement
if(ea >= 0b011'000'00 && ea <= 0b100'111'11) {
r.a(ea >> 2) = address;
}
}

View File

@ -15,12 +15,34 @@ auto M68K::instruction() -> void {
}
M68K::M68K() {
#define bind(id, name, ...) \
assert(!instructionTable[id]); \
instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \
disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); }; \
//ADD
for(uint3 d : range(8))
for(uint1 direction : range(2))
for(uint3 mode : range(8))
for(uint3 reg : range(8)) {
auto opcode = 0b1101'0000'0000'0000 | d << 9 | direction << 8 | mode << 3 | reg << 0;
if(direction == 1 && (mode == 0 || mode == 1 || (mode == 7 && reg >= 2))) continue;
EA ea{mode, reg};
bind(opcode | 0 << 6, ADD<Byte>, d, direction, ea);
bind(opcode | 1 << 6, ADD<Word>, d, direction, ea);
bind(opcode | 2 << 6, ADD<Long>, d, direction, ea);
}
#undef bind
#define match(pattern) if( \
(opcode & std::integral_constant<uint16_t, bit::mask(pattern)>::value) \
== std::integral_constant<uint16_t, bit::test(pattern)>::value \
)
#define bind(name, ...) \
assert(!instructionTable[opcode]); \
instructionTable[opcode] = [=] { return instruction##name(__VA_ARGS__); }; \
disassembleTable[opcode] = [=] { return disassemble##name(__VA_ARGS__); }; \
@ -29,15 +51,31 @@ M68K::M68K() {
for(uint16 opcode : range(65536)) {
/*
//ADD
match("1101 ---- ---- ----") {
uint3 r = bits(11,9);
uint1 direction = bit(8);
uint2 size = bits(7,6);
uint3 mode = bits(5,3);
uint3 reg = bits(2,0);
if(size != 3 && (direction == 0 || (mode == 2 || mode == 3 || mode == 4 || mode == 5 || mode == 6 || (mode == 7 && reg <= 1)))) {
uint8 ea = mode << 5 | reg << 2 | size;
bind(ADD, r, direction, ea);
}
}
*/
//ANDI
match("0000 0010 ---- ----") {
uint2 size = bits(7,6);
uint3 mode = bits(5,3);
uint3 reg = bits(2,0);
size = size == 0 ? Byte : size == 1 ? Word : size == 2 ? Long : 0;
if(size && mode != 1) {
bind(ANDI, size, mode, reg);
if(size != 3 && mode != 1) {
uint8 ea = mode << 5 | reg << 2 | size;
bind(ANDI, ea);
}
}
@ -58,48 +96,50 @@ M68K::M68K() {
uint3 reg = bits(2,0);
if(mode == 2 || mode == 5 || mode == 6 || (mode == 7 && reg <= 4)) {
bind(LEA, target, mode, reg);
uint8 ea = mode << 5 | reg << 2 | Long;
bind(LEA, target, ea);
}
}
//MOVE
match("00-- ---- ---- ----") {
uint2 size = bits(13,12);
uint2 size = bits(13,12) == 1 ? Byte : bits(13,12) == 3 ? Word : bits(13,12) == 2 ? Long : 3;
uint3 targetReg = bits(11,9);
uint3 targetMode = bits(8,6);
uint3 sourceMode = bits(5,3);
uint3 sourceReg = bits(2,0);
size = size == 1 ? Byte : size == 3 ? Word : size == 2 ? Long : 0;
if(size && targetMode != 1) {
bind(MOVE, size, targetReg, targetMode, sourceMode, sourceReg);
if(size != 3 && targetMode != 1) {
uint8 to = targetMode << 5 | targetReg << 2 | size;
uint8 from = sourceMode << 5 | sourceReg << 2 | size;
bind(MOVE, to, from);
}
}
//MOVEA
match("00-- ---0 01-- ----") {
uint2 size = bits(13,12);
uint3 target = bits(11,9);
uint2 size = bits(13,12) == 3 ? Word : bits(13,12) == 2 ? Long : 3;
uint3 to = bits(11,9);
uint3 mode = bits(5,3);
uint3 reg = bits(2,0);
size = size == 3 ? Word : size == 2 ? Long : 0;
if(size) {
bind(MOVEA, size, target, mode, reg);
if(size != 3) {
uint8 from = mode << 5 | reg << 2 | size;
bind(MOVEA, to, from);
}
}
//MOVEM
match("0100 1-00 1--- ----") {
uint1 direction = bit(10);
uint2 size = bit(6);
uint2 size = 1 + bit(6);
uint3 mode = bits(5,3);
uint3 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);
uint8 ea = mode << 5 | reg << 2 | size;
bind(MOVEM, direction, ea);
}
}
@ -136,22 +176,22 @@ M68K::M68K() {
uint3 mode = bits(5,3);
uint3 reg = bits(2,0);
size = size == 0 ? Byte : size == 1 ? Word : size == 2 ? Long : 0;
if(size) {
bind(TST, size, mode, reg);
if(size != 3) {
uint8 ea = mode << 5 | reg << 2 | size << 0;
bind(TST, ea);
}
}
}
for(uint16 opcode : range(65536)) {
if(instructionTable[opcode]) continue;
instructionTable[opcode] = [=] { trap(); };
disassembleTable[opcode] = [=] { return string{"???"}; };
}
#undef match
#undef bind
#undef bit
#undef bits
for(uint16 opcode : range(65536)) {
if(instructionTable[opcode]) continue;
instructionTable[opcode] = [=] { trap(); };
disassembleTable[opcode] = [=] { return string{"???"}; };
}
}

View File

@ -22,15 +22,75 @@ auto M68K::testCondition(uint4 condition) -> bool {
//
auto M68K::instructionANDI(uint2 size, uint3 mode, uint3 reg) -> void {
auto data = readPC(size);
EA modify{this, size, mode, reg};
modify.write(data = modify.read() & data);
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::carry(uint32 result, uint32 source) -> bool {
return clip<Size>(result) < clip<Size>(source);
}
template<uint Size> auto M68K::overflow(uint32 result, uint32 source, uint32 target) -> bool {
return sign<Size>((target ^ source) & (target ^ result)) < 0;
}
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;
}
auto M68K::zero(uint2 size, uint32 result) -> bool {
static const uint32 mask[4] = {0xff, 0xffff, 0xffffffff};
return (result & mask[size]) == 0;
}
auto M68K::negative(uint2 size, uint32 result) -> bool {
static const uint32 mask[4] = {0x80, 0x8000, 0x80000000};
return result & mask[size];
}
//
template<uint Size> auto M68K::instructionADD(uint3 reg, uint1 direction, EA ea) -> void {
uint32 source;
uint32 target;
uint32 result;
if(direction == 0) {
source = read<Size>(ea);
target = r.d(reg);
result = source + target;
r.d(reg) = result;
} else {
source = r.d(reg);
target = read<Size>(ea);
result = source + target;
write<Size>(ea, result);
}
r.c = carry<Size>(result, source);
r.v = overflow<Size>(result, source, target);
r.z = zero<Size>(result);
r.n = negative<Size>(result);
r.x = r.c;
}
auto M68K::instructionANDI(uint8 ea) -> void {
auto result = modify(ea, readPC(ea), [&](auto x, auto y) -> uint32 {
return x & y;
});
r.c = 0;
r.v = 0;
r.z = data == 0;
r.n = sign(size, data) < 0;
r.z = zero(ea, result);
r.n = negative(ea, result);
}
auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
@ -44,41 +104,45 @@ auto M68K::instructionBCC(uint4 condition, uint8 displacement) -> void {
if(testCondition(condition)) r.pc += displacement;
}
auto M68K::instructionLEA(uint3 target, uint3 mode, uint3 reg) -> void {
EA source{this, Long, mode, reg};
r.a(target) = source.address;
auto M68K::instructionLEA(uint3 target, uint8 ea) -> void {
r.a(target) = address(ea);
}
auto M68K::instructionMOVE(uint2 size, uint3 targetReg, uint3 targetMode, uint3 sourceMode, uint3 sourceReg) -> void {
EA source{this, size, sourceMode, sourceReg};
EA target{this, size, targetMode, targetReg};
auto data = source.read();
source.flush();
target.write(data);
auto M68K::instructionMOVE(uint8 to, uint8 from) -> void {
auto data = read(from);
write(to, data);
r.c = 0;
r.v = 0;
r.z = data == 0;
r.n = sign(size, data) < 0;
r.z = zero(from, data);
r.n = negative(from, data);
}
auto M68K::instructionMOVEA(uint2 size, uint3 target, uint3 mode, uint3 reg) -> void {
EA source{this, size, mode, reg};
r.a(target) = source.read();
auto M68K::instructionMOVEA(uint3 to, uint8 from) -> void {
auto data = read(from);
if(from & 1) data = (int16)data;
r.a(to) = data;
}
auto M68K::instructionMOVEM(uint1 direction, uint2 size, uint3 mode, uint3 reg) -> void {
auto M68K::instructionMOVEM(uint1 direction, uint8 ea) -> void {
auto list = readPC();
EA source{this, size, mode, reg};
auto addr = address(ea);
for(uint n : range(8)) {
if(list.bit(0 + n)) r.d(n) = source.read();
if(list.bit(0 + n)) {
r.d(n) = readAbsolute(ea, addr);
addr += 2 + (ea & 2);
}
}
for(uint n : range(8)) {
if(list.bit(8 + n)) r.a(n) = source.read();
if(list.bit(8 + n)) {
r.a(n) = readAbsolute(ea, addr);
addr += 2 + (ea & 2);
}
}
flush(ea, addr);
}
auto M68K::instructionMOVEQ(uint3 target, uint8 immediate) -> void {
@ -87,7 +151,7 @@ auto M68K::instructionMOVEQ(uint3 target, uint8 immediate) -> void {
r.c = 0;
r.v = 0;
r.z = immediate == 0;
r.v = sign(Byte, immediate) < 0;
r.n = negative(Byte, immediate);
}
auto M68K::instructionMOVE_USP(uint1 direction, uint3 reg) -> void {
@ -102,12 +166,11 @@ auto M68K::instructionMOVE_USP(uint1 direction, uint3 reg) -> void {
auto M68K::instructionNOP() -> void {
}
auto M68K::instructionTST(uint2 size, uint3 mode, uint3 reg) -> void {
EA source{this, size, mode, reg};
auto data = source.read();
auto M68K::instructionTST(uint8 ea) -> void {
auto data = read(ea);
r.c = 0;
r.v = 0;
r.z = data == 0;
r.n = sign(size, data) < 0;
r.z = zero(ea, data);
r.n = negative(ea, data);
}

View File

@ -3,6 +3,8 @@
namespace Processor {
enum : uint { Byte, Word, Long };
#include "registers.cpp"
#include "memory.cpp"
#include "ea.cpp"
@ -24,11 +26,4 @@ auto M68K::reset() -> void {
r.sr = 0x2000;
}
auto M68K::sign(uint2 size, uint32 data) -> int32 {
if(size == Byte) return (int8)data;
if(size == Word) return (int16)data;
if(size == Long) return (int32)data;
return 0;
}
}

View File

@ -5,7 +5,7 @@
namespace Processor {
struct M68K {
enum : uint { Byte = 1, Word = 2, Long = 3 };
enum : uint { Byte, Word, Long };
M68K();
@ -16,39 +16,33 @@ struct M68K {
auto power() -> void;
auto reset() -> void;
auto sign(uint2 size, uint32 data) -> int32;
//memory.cpp
auto readAbsolute(uint2 size, uint32 addr) -> uint32;
auto writeAbsolute(uint2 size, uint32 addr, uint32 data) -> void;
auto readPC(uint2 size = Word) -> uint32;
template<uint Size> auto read(uint32 addr) -> uint32;
//ea.cpp
struct EA {
EA(M68K* self, uint2 size, uint3 mode, uint3 reg);
~EA();
EA(uint3 mode, uint3 reg) : mode(mode), reg(reg) {
if(mode == 7) mode += reg; //optimization: convert modes {7; 0-4} to {8-11}
}
auto pc() -> uint32&;
auto d(uint3 reg) -> uint32&;
auto a(uint3 reg) -> uint32&;
auto readPC(uint2 size) -> uint32;
auto read(uint32 addr) -> uint32;
auto write(uint32 addr, uint32 data) -> void;
auto fetch() -> uint32;
auto read() -> uint32;
auto write(uint32 data) -> void;
auto flush() -> void;
uint2 size;
uint4 mode;
uint3 reg;
uint32 address;
M68K* self;
};
template<uint Size> auto read(EA ea) -> uint32;
template<uint Size> auto write(EA ea, uint32 data) -> void;
auto address(uint8 ea) -> uint32;
auto read(uint8 ea) -> uint32;
auto write(uint8 ea, uint32 data) -> void;
auto modify(uint8 ea, uint32 data, const function<uint32 (uint32, uint32)>& op) -> uint32;
auto flush(uint8 ea, uint32 address) -> void;
//instruction.cpp
auto trap() -> void;
auto instruction() -> void;
@ -56,16 +50,27 @@ struct M68K {
//instructions.cpp
auto testCondition(uint4 condition) -> bool;
auto instructionANDI(uint2 size, uint3 mode, uint3 reg) -> void;
template<uint Size> auto clip(uint32 data) -> uint32;
template<uint Size> auto sign(uint32 data) -> int32;
template<uint Size> auto carry(uint32 result, uint32 source) -> bool;
template<uint Size> auto overflow(uint32 result, uint32 source, uint32 target) -> bool;
template<uint Size> auto zero(uint32 result) -> bool;
template<uint Size> auto negative(uint32 result) -> bool;
auto zero(uint2 size, uint32 result) -> bool;
auto negative(uint2 size, uint32 result) -> bool;
template<uint Size> auto instructionADD(uint3 reg, uint1 direction, EA ea) -> void;
auto instructionANDI(uint8 ea) -> void;
auto instructionBCC(uint4 condition, uint8 displacement) -> void;
auto instructionLEA(uint3 target, uint3 mode, uint3 reg) -> void;
auto instructionMOVE(uint2 size, uint3 targetReg, uint3 targetMode, uint3 sourceMode, uint3 sourceReg) -> void;
auto instructionMOVEA(uint2 size, uint3 target, uint3 mode, uint3 reg) -> void;
auto instructionMOVEM(uint1 direction, uint2 size, uint3 mode, uint3 reg) -> void;
auto instructionLEA(uint3 target, uint8 source) -> void;
auto instructionMOVE(uint8 to, uint8 from) -> void;
auto instructionMOVEA(uint3 to, uint8 ea) -> void;
auto instructionMOVEM(uint1 direction, uint8 ea) -> void;
auto instructionMOVEQ(uint3 target, uint8 immediate) -> void;
auto instructionMOVE_USP(uint1 direction, uint3 reg) -> void;
auto instructionNOP() -> void;
auto instructionTST(uint2 size, uint3 mode, uint3 reg) -> void;
auto instructionTST(uint8 ea) -> void;
//disassembler.cpp
auto disassemble(uint32 pc) -> string;
@ -101,27 +106,28 @@ struct M68K {
private:
//disassembler.cpp
auto disassembleANDI(uint2 size, uint3 mode, uint3 reg) -> string;
template<uint Size> auto disassembleADD(uint3 reg, uint1 direction, EA ea) -> string;
auto disassembleANDI(uint8 ea) -> string;
auto disassembleBCC(uint4 condition, uint8 displacement) -> string;
auto disassembleLEA(uint3 target, uint3 mode, uint3 reg) -> string;
auto disassembleMOVE(uint2 size, uint3 targetReg, uint3 targetMode, uint3 sourceMode, uint3 sourceReg) -> string;
auto disassembleMOVEA(uint2 size, uint3 target, uint3 mode, uint3 reg) -> string;
auto disassembleMOVEM(uint1 direction, uint2 size, uint3 mode, uint3 reg) -> string;
auto disassembleLEA(uint3 target, uint8 ea) -> string;
auto disassembleMOVE(uint8 to, uint8 from) -> string;
auto disassembleMOVEA(uint3 to, uint8 from) -> string;
auto disassembleMOVEM(uint1 direction, uint8 ea) -> string;
auto disassembleMOVEQ(uint3 target, uint8 immediate) -> string;
auto disassembleMOVE_USP(uint1 direction, uint3 reg) -> string;
auto disassembleNOP() -> string;
auto disassembleTST(uint2 size, uint3 mode, uint3 reg) -> string;
auto disassembleTST(uint8 ea) -> 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(uint2 size, uint3 mode, uint3 reg) -> string;
auto _read(uint2 size, uint3 mode, uint3 reg) -> string;
auto _write(uint2 size, uint3 mode, uint3 reg) -> string;
auto _readPC(uint2 size = Word) -> uint32;
auto _immediate(uint2 size) -> string;
auto _address(uint8 ea) -> string;
auto _read(uint8 ea) -> string;
auto _write(uint8 ea) -> string;
auto _branch(uint8 displacement) -> string;
auto _suffix(uint size) -> string;
auto _suffix(uint2 size) -> string;
auto _condition(uint4 condition) -> string;
uint32 _pc;

View File

@ -30,3 +30,22 @@ auto M68K::readPC(uint2 size) -> uint32 {
r.pc += 2;
return data;
}
//
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;
}

View File

@ -14,7 +14,7 @@ template<typename R, typename... P> struct function<auto (P...) -> R> {
static constexpr bool value = decltype(exists<L>(0))::value;
};
function() = default;
function() {}
function(const function& source) { operator=(source); }
function(void* function) { if(function) callback = new global((auto (*)(P...) -> R)function); }
function(auto (*function)(P...) -> R) { callback = new global(function); }