mirror of https://github.com/bsnes-emu/bsnes.git
Update to v100r09 release.
byuu says: Another six hours in ... I have all of the opcodes, memory access functions, disassembler mnemonics and table building converted over to the new template<uint Size> format. Certainly, it would be quite easy for this nightmare chip to throw me another curveball, but so far I can handle: - MOVE (EA to, EA from) case - read(from) has to update register index for +/-(aN) mode - MOVEM (EA from) case - when using +/-(aN), RA can't actually be updated until the transfer is completed - LEA (EA from) case - doesn't actually perform the final read; just returns the address to be read from - ANDI (EA from-and-to) case - same EA has to be read from and written to - for -(aN), the read has to come from aN-2, but can't update aN yet; so that the write also goes to aN-2 - no opcode can ever fetch the extension words more than once - manually control the order of extension word fetching order for proper opcode decoding To do all of that without a whole lot of duplicated code (or really bloating out every single instruction with red tape), I had to bring back the "bool valid / uint32 address" variables inside the EA struct =( If weird exceptions creep in like timing constraints only on certain opcodes, I can use template flags to the EA read/write functions to handle that.
This commit is contained in:
parent
92fe5b0813
commit
be3f6ac0d5
|
@ -11,7 +11,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "100.08";
|
||||
static const string Version = "100.09";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -10,8 +10,8 @@ auto CPU::Enter() -> void {
|
|||
}
|
||||
|
||||
auto CPU::boot() -> void {
|
||||
r.ssp = readAbsolute(Long, 0);
|
||||
r.pc = readAbsolute(Long, 4);
|
||||
r.ssp = read(1, 0) << 16 | read(1, 2) << 0;
|
||||
r.pc = read(1, 4) << 16 | read(1, 6) << 0;
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
|
|
|
@ -1,68 +1,53 @@
|
|||
auto M68K::_readByte(uint32 addr) -> uint8 {
|
||||
template<> auto M68K::_read<Byte>(uint32 addr) -> uint32 {
|
||||
return read(0, addr);
|
||||
}
|
||||
|
||||
auto M68K::_readWord(uint32 addr) -> uint16 {
|
||||
template<> auto M68K::_read<Word>(uint32 addr) -> uint32 {
|
||||
return read(1, addr);
|
||||
}
|
||||
|
||||
auto M68K::_readLong(uint32 addr) -> uint32 {
|
||||
uint32 data = _readWord(addr + 0) << 16;
|
||||
return data |= _readWord(addr + 2) << 0;
|
||||
template<> auto M68K::_read<Long>(uint32 addr) -> uint32 {
|
||||
uint32 data = _read<Word>(addr + 0) << 16;
|
||||
return data | _read<Word>(addr + 2) << 0;
|
||||
}
|
||||
|
||||
auto M68K::_readPC(uint2 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;
|
||||
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::_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::_register(Register r) -> string {
|
||||
static const string registers[16] = {
|
||||
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
|
||||
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||
};
|
||||
return registers[r.number];
|
||||
}
|
||||
|
||||
auto M68K::_address(uint8 ea) -> string {
|
||||
uint3 mode = ea >> 5;
|
||||
uint3 reg = ea >> 2;
|
||||
uint2 size = ea >> 0;
|
||||
template<uint Size> auto M68K::_immediate() -> string {
|
||||
return {"#$", hex(_readPC<Size>(), 2 << Size)};
|
||||
}
|
||||
|
||||
if(mode == 7) {
|
||||
if(reg == 2) return {"$", hex(_pc + (int16)_readPC(Word), 6L)};
|
||||
}
|
||||
template<uint Size> auto M68K::_address(EA ea) -> string {
|
||||
if(ea.mode == 9) return {"$", hex(_pc + (int16)_readPC(), 6L)};
|
||||
return "???";
|
||||
}
|
||||
|
||||
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, ")"};
|
||||
if(mode == 3) return {"(a", reg, ")+"};
|
||||
if(mode == 4) return {"-(a", reg, ")"};
|
||||
if(mode == 5) return {"($", hex(r.a(reg) + (int16)_readPC(Word), 6L), ")"};
|
||||
if(mode == 7) {
|
||||
if(reg == 1) return {"($", hex(_readPC(Long), 6L), ")"};
|
||||
if(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)};
|
||||
}
|
||||
}
|
||||
template<uint Size> auto M68K::_read(EA ea) -> string {
|
||||
if(ea.mode == 0) return {_register(ea.reg)};
|
||||
if(ea.mode == 1) return {_register(ea.reg)};
|
||||
if(ea.mode == 2) return {"(", _register(ea.reg), ")"};
|
||||
if(ea.mode == 3) return {"(", _register(ea.reg), ")+"};
|
||||
if(ea.mode == 4) return {"-(", _register(ea.reg), ")"};
|
||||
if(ea.mode == 5) return {"($", hex(read(ea.reg) + (int16)_readPC(), 6L), ")"};
|
||||
if(ea.mode == 8) return {"($", hex(_readPC<Long>(), 6L), ")"};
|
||||
if(ea.mode == 11) return {"#$", hex(_readPC<Size>(), 2 << Size)};
|
||||
return "???";
|
||||
}
|
||||
|
||||
auto M68K::_write(uint8 ea) -> string {
|
||||
return _read(ea);
|
||||
template<uint Size> auto M68K::_write(EA ea) -> string {
|
||||
return _read<Size>(ea);
|
||||
}
|
||||
|
||||
auto M68K::_branch(uint8 displacement) -> string {
|
||||
|
@ -72,11 +57,8 @@ auto M68K::_branch(uint8 displacement) -> string {
|
|||
return {"$", hex(_pc + displacement, 6L)};
|
||||
}
|
||||
|
||||
auto M68K::_suffix(uint2 size) -> string {
|
||||
if(size == Byte) return ".b";
|
||||
if(size == Word) return ".w";
|
||||
if(size == Long) return ".l";
|
||||
return ".?";
|
||||
template<uint Size> auto M68K::_suffix() -> string {
|
||||
return Size == Byte ? ".b" : Size == Word ? ".w" : ".l";
|
||||
}
|
||||
|
||||
auto M68K::_condition(uint4 condition) -> string {
|
||||
|
@ -111,64 +93,60 @@ auto M68K::disassembleRegisters() -> string {
|
|||
|
||||
//
|
||||
|
||||
template<uint Size> auto M68K::disassembleADD(uint3 reg, uint1 direction, EA ea) -> string {
|
||||
string op{"add", _suffix(ea.reg), " "};
|
||||
return op;
|
||||
template<uint Size> auto M68K::disassembleADD(Register rd, uint1 direction, EA ea) -> string {
|
||||
string op{"add", _suffix<Size>(), " "};
|
||||
|
||||
if(direction == 0) {
|
||||
// return {op, _read(ea), ",d", reg};
|
||||
return {op, _read<Size>(ea), ",", _register(rd)};
|
||||
} else {
|
||||
// return {op, "d", reg, ",", _read(ea)};
|
||||
return {op, "", _register(rd), ",", _read<Size>(ea)};
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::disassembleANDI(uint8 ea) -> string {
|
||||
return {"andi", _suffix(ea), " ", _immediate(ea), ",", _read(ea)};
|
||||
template<uint Size> auto M68K::disassembleANDI(EA ea) -> string {
|
||||
return {"andi", _suffix<Size>(), " ", _immediate<Size>(), ",", _read<Size>(ea)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleBCC(uint4 condition, uint8 displacement) -> string {
|
||||
return {"b", _condition(condition), " ", _branch(displacement)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleLEA(uint3 to, uint8 ea) -> string {
|
||||
return {"lea ", _address(ea), ",a", to};
|
||||
auto M68K::disassembleLEA(Register ra, EA ea) -> string {
|
||||
return {"lea ", _address<Long>(ea), ",", _register(ra)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE(uint8 to, uint8 from) -> string {
|
||||
return {"move", _suffix(from), " ", _read(from), ",", _write(to)};
|
||||
template<uint Size> auto M68K::disassembleMOVE(EA to, EA from) -> string {
|
||||
return {"move", _suffix<Size>(), " ", _read<Size>(from), ",", _write<Size>(to)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEA(uint3 to, uint8 from) -> string {
|
||||
return {"movea ", _read(from), ",a", to};
|
||||
template<uint Size> auto M68K::disassembleMOVEA(Register ra, EA ea) -> string {
|
||||
return {"movea ", _read<Size>(ea), ",", _register(ra)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEM(uint1 direction, uint8 ea) -> string {
|
||||
string op{"movem", _suffix(ea), " "};
|
||||
template<uint Size> auto M68K::disassembleMOVEM(uint1 direction, EA ea) -> 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, ",");
|
||||
for(uint rn : range(16)) if(list.bit(rn)) regs.append(_register(Register{rn}), ",");
|
||||
regs.trimRight(",");
|
||||
|
||||
if(direction == 0) {
|
||||
return {op, regs, ",", _read(ea)};
|
||||
return {op, regs, ",", _read<Size>(ea)};
|
||||
} else {
|
||||
return {op, _read(ea), ",", regs};
|
||||
return {op, _read<Size>(ea), ",", regs};
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVEQ(uint3 target, uint8 immediate) -> string {
|
||||
return {"moveq #$", hex(immediate, 2L), ",d", target};
|
||||
auto M68K::disassembleMOVEQ(Register rd, uint8 immediate) -> string {
|
||||
return {"moveq #$", hex(immediate, 2L), ",", _register(rd)};
|
||||
}
|
||||
|
||||
auto M68K::disassembleMOVE_USP(uint1 direction, uint3 reg) -> string {
|
||||
auto M68K::disassembleMOVE_USP(uint1 direction, Register ra) -> string {
|
||||
if(direction == 0) {
|
||||
return {"move a", reg, ",usp"};
|
||||
return {"move ", _register(ra), ",usp"};
|
||||
} else {
|
||||
return {"move usp,a", reg};
|
||||
return {"move usp,", _register(ra)};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +154,6 @@ auto M68K::disassembleNOP() -> string {
|
|||
return {"nop "};
|
||||
}
|
||||
|
||||
auto M68K::disassembleTST(uint8 ea) -> string {
|
||||
return {"tst", _suffix(ea), " ", _read(ea)};
|
||||
template<uint Size> auto M68K::disassembleTST(EA ea) -> string {
|
||||
return {"tst", _suffix<Size>(), " ", _read<Size>(ea)};
|
||||
}
|
||||
|
|
|
@ -1,423 +1,203 @@
|
|||
//effective addressing
|
||||
template<uint Size> auto M68K::fetch(EA& ea) -> uint32 {
|
||||
ea.valid = true;
|
||||
|
||||
//encoding:
|
||||
// d7-d5: mode
|
||||
// d4-d2: register
|
||||
// d1-d0: size (0 = byte, 1 = word, 2 = long)
|
||||
switch(ea.mode) {
|
||||
|
||||
auto M68K::address(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:
|
||||
return r.a(ea >> 2);
|
||||
|
||||
//address register indirect with post-increment
|
||||
case 0b011'000'00 ... 0b011'111'11:
|
||||
return r.a(ea >> 2);
|
||||
|
||||
//address register indirect with pre-decrement
|
||||
case 0b100'000'00 ... 0b100'111'11:
|
||||
return r.a(ea >> 2);
|
||||
|
||||
//address register indirect with displacement
|
||||
case 0b101'000'00 ... 0b101'111'11:
|
||||
return 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 r.a(ea >> 2) + index + (int8)word;
|
||||
case 0: { //data register direct
|
||||
return read(ea.reg);
|
||||
}
|
||||
|
||||
//absolute short indirect
|
||||
case 0b111'000'00 ... 0b111'000'11:
|
||||
case 1: { //data register indirect
|
||||
return read(ea.reg);
|
||||
}
|
||||
|
||||
case 2: { //address register indirect
|
||||
return read(ea.reg);
|
||||
}
|
||||
|
||||
case 3: { //address register indirect with post-increment
|
||||
return read(ea.reg);
|
||||
}
|
||||
|
||||
case 4: { //address register indirect with pre-decrement
|
||||
return read(ea.reg);
|
||||
}
|
||||
|
||||
case 5: { //address register indirect with displacement
|
||||
return read(ea.reg) + (int16)readPC();
|
||||
}
|
||||
|
||||
case 6: { //address register indirect with index
|
||||
auto extension = readPC();
|
||||
auto index = read(Register{extension >> 12});
|
||||
if(extension & 0x800) index = (int16)index;
|
||||
return read(ea.reg) + index + (int8)extension;
|
||||
}
|
||||
|
||||
case 7: { //absolute short indirect
|
||||
return (int16)readPC();
|
||||
|
||||
//absolute long indirect
|
||||
case 0b111'001'00 ... 0b111'001'11: {
|
||||
uint32 address = readPC() << 16;
|
||||
return address | readPC() << 0;
|
||||
}
|
||||
|
||||
//program counter indirect with displacement
|
||||
case 0b111'010'00 ... 0b111'010'11: {
|
||||
case 8: { //absolute long indirect
|
||||
return readPC<Long>();
|
||||
}
|
||||
|
||||
case 9: { //program counter indirect with displacement
|
||||
auto base = r.pc;
|
||||
return base + (int16)readPC();
|
||||
}
|
||||
|
||||
//program counter indirect with index
|
||||
case 0b111'011'00 ... 0b111'011'11: {
|
||||
case 10: { //program counter indirect with index
|
||||
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;
|
||||
auto extension = readPC();
|
||||
auto index = read(Register{extension >> 12});
|
||||
if(extension & 0x800) index = (int16)index;
|
||||
return base + index + (int8)extension;
|
||||
}
|
||||
|
||||
//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;
|
||||
case 11: { //immediate
|
||||
return readPC<Size>();
|
||||
}
|
||||
|
||||
//invalid
|
||||
default:
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<uint Size> auto M68K::read(EA ea) -> uint32 {
|
||||
template<uint Size, bool Update> auto M68K::read(EA& ea) -> uint32 {
|
||||
if(!ea.valid) ea.address = fetch<Size>(ea);
|
||||
|
||||
switch(ea.mode) {
|
||||
|
||||
case 0: { //data register direct
|
||||
return clip<Size>(r.d(ea.reg));
|
||||
return clip<Size>(ea.address);
|
||||
}
|
||||
|
||||
case 1: { //address register direct
|
||||
return clip<Size>(r.a(ea.reg));
|
||||
return clip<Size>(ea.address);
|
||||
}
|
||||
|
||||
case 2: { //address register indirect
|
||||
auto& address = r.a(ea.reg);
|
||||
return read<Size>(address);
|
||||
return read<Size>(ea.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;
|
||||
auto data = read<Size>(ea.address);
|
||||
if(Update) write(ea.reg, ea.address += (Size == Long ? 4 : 2));
|
||||
return data;
|
||||
}
|
||||
|
||||
case 4: { //address register indirect with pre-decrement
|
||||
auto& address = r.a(ea.reg);
|
||||
address -= Size == Long ? 4 : 2;
|
||||
return read<Size>(address);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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 1: { //address register direct
|
||||
r.a(ea.reg) = data;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
case 0b011'000'00 ... 0b011'111'11: {
|
||||
auto& address = r.a(ea >> 2);
|
||||
auto data = readAbsolute(ea, address);
|
||||
address += 2 + (ea & 2);
|
||||
auto data = read<Size>((uint32)(ea.address - (Size == Long ? 4 : 2)));
|
||||
if(Update) write(ea.reg, ea.address -= (Size == Long ? 4 : 2));
|
||||
return data;
|
||||
}
|
||||
|
||||
//address register indirect with pre-decrement
|
||||
case 0b100'000'00 ... 0b100'111'11: {
|
||||
auto& address = r.a(ea >> 2);
|
||||
address -= 2 + (ea & 2);
|
||||
return readAbsolute(ea, address);
|
||||
case 5: { //address register indirect with displacement
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
//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);
|
||||
case 6: { //address register indirect with index
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
//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());
|
||||
case 7: { //absolute short indirect
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
//program counter indirect with displacement
|
||||
case 0b111'010'00 ... 0b111'010'11: {
|
||||
auto base = r.pc;
|
||||
return readAbsolute(ea, base + (int16)readPC());
|
||||
case 8: { //absolute long indirect
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
//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);
|
||||
case 9: { //program counter indirect with displacement
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
//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;
|
||||
case 10: { //program counter indirect with index
|
||||
return read<Size>(ea.address);
|
||||
}
|
||||
|
||||
//invalid
|
||||
default:
|
||||
return 0;
|
||||
case 11: { //immediate
|
||||
return clip<Size>(ea.address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto M68K::write(uint8 ea, uint32 data) -> void {
|
||||
switch(ea) {
|
||||
template<uint Size, bool Update> auto M68K::write(EA& ea, uint32 data) -> void {
|
||||
if(!ea.valid) ea.address = fetch<Size>(ea);
|
||||
|
||||
//data register direct
|
||||
case 0b000'000'00 ... 0b000'111'11:
|
||||
r.d(ea >> 2) = data;
|
||||
return;
|
||||
switch(ea.mode) {
|
||||
|
||||
//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);
|
||||
case 0: { //data register direct
|
||||
return write<Size>(ea.reg, 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);
|
||||
case 1: { //address register direct
|
||||
return write<Size>(ea.reg, data);
|
||||
}
|
||||
|
||||
case 2: { //address register indirect
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case 3: { //address register indirect with post-increment
|
||||
write<Size>(ea.address, data);
|
||||
if(Update) write(ea.reg, ea.address += (Size == Long ? 4 : 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);
|
||||
case 4: { //address register indirect with pre-decrement
|
||||
write<Size>((uint32)(ea.address - (Size == Long ? 4 : 2)), data);
|
||||
if(Update) write(ea.reg, ea.address -= (Size == Long ? 4 : 2));
|
||||
return;
|
||||
}
|
||||
|
||||
//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);
|
||||
case 5: { //address register indirect with displacement
|
||||
return write<Size>(ea.address, 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);
|
||||
case 6: { //address register indirect with index
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
//program counter indirect with displacement
|
||||
case 0b111'010'00 ... 0b111'010'11: {
|
||||
auto base = r.pc;
|
||||
return writeAbsolute(ea, base + (int16)readPC(), data);
|
||||
case 7: { //absolute short indirect
|
||||
return write<Size>(ea.address, 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);
|
||||
case 8: { //absolute long indirect
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case 9: { //program counter indirect with displacement
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case 10: { //program counter indirect with index
|
||||
return write<Size>(ea.address, data);
|
||||
}
|
||||
|
||||
case 11: { //immediate
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::modify(uint8 ea, uint32 data, const function<uint32 (uint32, uint32)>& op) -> uint32 {
|
||||
switch(ea) {
|
||||
template<uint Size> auto M68K::flush(EA& ea, uint32 data) -> void {
|
||||
switch(ea.mode) {
|
||||
|
||||
//data register direct
|
||||
case 0b000'000'00 ... 0b000'111'11: {
|
||||
auto& address = r.d(ea >> 2);
|
||||
return address = op(address, data);
|
||||
case 3: { //address register indirect with post-increment
|
||||
write<Size>(ea.reg, data);
|
||||
return;
|
||||
}
|
||||
|
||||
//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);
|
||||
case 4: { //address register indirect with pre-decrement
|
||||
write<Size>(ea.reg, data);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ auto M68K::trap() -> void {
|
|||
|
||||
auto M68K::instruction() -> void {
|
||||
instructionsExecuted++;
|
||||
print(disassembleRegisters(), "\n", disassemble(r.pc), "\n\n");
|
||||
print(disassembleRegisters(), "\n");
|
||||
print(disassemble(r.pc), "\n");
|
||||
print("\n");
|
||||
|
||||
opcode = readPC();
|
||||
return instructionTable[opcode]();
|
||||
|
@ -18,180 +20,141 @@ M68K::M68K() {
|
|||
#define bind(id, name, ...) \
|
||||
assert(!instructionTable[id]); \
|
||||
instructionTable[id] = [=] { return instruction##name(__VA_ARGS__); }; \
|
||||
disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); }; \
|
||||
disassembleTable[id] = [=] { return disassemble##name(__VA_ARGS__); };
|
||||
|
||||
#define pattern(s) \
|
||||
std::integral_constant<uint16_t, bit::test(s)>::value
|
||||
|
||||
//ADD
|
||||
for(uint3 d : range(8))
|
||||
for(uint3 dreg : 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;
|
||||
auto opcode = pattern("1101 ---- ++-- ----") | dreg << 9 | direction << 8 | mode << 3 | reg << 0;
|
||||
if(direction == 1 && (mode == 0 || mode == 1 || (mode == 7 && reg >= 2))) continue;
|
||||
|
||||
Register rd{0u + dreg};
|
||||
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);
|
||||
bind(opcode | 0 << 6, ADD<Byte>, rd, direction, ea);
|
||||
bind(opcode | 1 << 6, ADD<Word>, rd, direction, ea);
|
||||
bind(opcode | 2 << 6, ADD<Long>, rd, direction, ea);
|
||||
}
|
||||
|
||||
//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 >= 4)) continue;
|
||||
|
||||
EA ea{mode, reg};
|
||||
bind(opcode | 0 << 6, ANDI<Byte>, ea);
|
||||
bind(opcode | 1 << 6, ANDI<Word>, ea);
|
||||
bind(opcode | 2 << 6, ANDI<Long>, ea);
|
||||
}
|
||||
|
||||
//BCC
|
||||
for(uint4 condition : range( 16))
|
||||
for(uint8 displacement : range(256)) {
|
||||
auto opcode = pattern("0110 ---- ---- ----") | condition << 8 | displacement << 0;
|
||||
|
||||
bind(opcode, BCC, condition, displacement);
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
Register ra{8u + areg};
|
||||
EA ea{mode, reg};
|
||||
bind(opcode, LEA, ra, 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 >= 4)) continue;
|
||||
|
||||
EA to{toMode, toReg};
|
||||
EA 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);
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
Register ra{8u + areg};
|
||||
EA ea{mode, reg};
|
||||
bind(opcode | 3 << 12, MOVEA<Word>, ra, ea);
|
||||
bind(opcode | 2 << 12, MOVEA<Long>, ra, 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(mode <= 1 || mode == 3 || (mode == 7 && reg >= 4));
|
||||
|
||||
EA 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;
|
||||
|
||||
Register rd{0u + dreg};
|
||||
bind(opcode, MOVEQ, rd, immediate);
|
||||
}
|
||||
|
||||
//MOVE_USP
|
||||
for(uint1 direction : range(2))
|
||||
for(uint3 areg : range(8)) {
|
||||
auto opcode = pattern("0100 1110 0110 ----") | direction << 3 | areg << 0;
|
||||
|
||||
Register ra{8u + areg};
|
||||
bind(opcode, MOVE_USP, direction, ra);
|
||||
}
|
||||
|
||||
//NOP
|
||||
{ auto opcode = pattern("0100 1110 0111 0001");
|
||||
|
||||
bind(opcode, NOP);
|
||||
}
|
||||
|
||||
//TST
|
||||
for(uint3 mode : range(8))
|
||||
for(uint3 reg : range(8)) {
|
||||
auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0;
|
||||
|
||||
EA ea{mode, reg};
|
||||
bind(opcode | 0 << 6, TST<Byte>, ea);
|
||||
bind(opcode | 1 << 6, TST<Word>, ea);
|
||||
bind(opcode | 2 << 6, TST<Long>, ea);
|
||||
}
|
||||
|
||||
#undef bind
|
||||
#undef pattern
|
||||
|
||||
#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__); }; \
|
||||
|
||||
#define bit(x) (uint)opcode.bit(x)
|
||||
#define bits(x, y) (uint)opcode.bits(x, y)
|
||||
|
||||
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);
|
||||
|
||||
if(size != 3 && mode != 1) {
|
||||
uint8 ea = mode << 5 | reg << 2 | size;
|
||||
bind(ANDI, ea);
|
||||
}
|
||||
}
|
||||
|
||||
//BCC
|
||||
match("0110 ---- ---- ----") {
|
||||
uint4 condition = bits(11,8);
|
||||
uint8 displacement = bits(7,0);
|
||||
|
||||
if(true) {
|
||||
bind(BCC, condition, displacement);
|
||||
}
|
||||
}
|
||||
|
||||
//LEA
|
||||
match("0100 ---1 11-- ----") {
|
||||
uint3 target = bits(11,9);
|
||||
uint3 mode = bits(5,3);
|
||||
uint3 reg = bits(2,0);
|
||||
|
||||
if(mode == 2 || mode == 5 || mode == 6 || (mode == 7 && reg <= 4)) {
|
||||
uint8 ea = mode << 5 | reg << 2 | Long;
|
||||
bind(LEA, target, ea);
|
||||
}
|
||||
}
|
||||
|
||||
//MOVE
|
||||
match("00-- ---- ---- ----") {
|
||||
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);
|
||||
|
||||
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) == 3 ? Word : bits(13,12) == 2 ? Long : 3;
|
||||
uint3 to = bits(11,9);
|
||||
uint3 mode = bits(5,3);
|
||||
uint3 reg = bits(2,0);
|
||||
|
||||
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 = 1 + bit(6);
|
||||
uint3 mode = bits(5,3);
|
||||
uint3 reg = bits(2,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)))) {
|
||||
uint8 ea = mode << 5 | reg << 2 | size;
|
||||
bind(MOVEM, direction, ea);
|
||||
}
|
||||
}
|
||||
|
||||
//MOVEQ
|
||||
match("0111 ---0 ---- ----") {
|
||||
uint3 target = bits(11,9);
|
||||
uint8 immediate = bits(7,0);
|
||||
|
||||
if(true) {
|
||||
bind(MOVEQ, target, immediate);
|
||||
}
|
||||
}
|
||||
|
||||
//MOVE_USP
|
||||
match("0100 1110 0110 ----") {
|
||||
uint1 direction = bit(3);
|
||||
uint3 reg = bits(2,0);
|
||||
|
||||
if(true) {
|
||||
bind(MOVE_USP, direction, reg);
|
||||
}
|
||||
}
|
||||
|
||||
//NOP
|
||||
match("0100 1110 0111 0001") {
|
||||
if(true) {
|
||||
bind(NOP);
|
||||
}
|
||||
}
|
||||
|
||||
//TST
|
||||
match("0100 1010 ---- ----") {
|
||||
uint2 size = bits(7,6);
|
||||
uint3 mode = bits(5,3);
|
||||
uint3 reg = bits(2,0);
|
||||
|
||||
if(size != 3) {
|
||||
uint8 ea = mode << 5 | reg << 2 | size << 0;
|
||||
bind(TST, ea);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef match
|
||||
#undef bind
|
||||
#undef bit
|
||||
#undef bits
|
||||
|
||||
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");
|
||||
}
|
||||
|
|
|
@ -46,30 +46,20 @@ 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 {
|
||||
template<uint Size> auto M68K::instructionADD(Register rd, uint1 direction, EA ea) -> void {
|
||||
uint32 source;
|
||||
uint32 target;
|
||||
uint32 result;
|
||||
|
||||
if(direction == 0) {
|
||||
source = read<Size>(ea);
|
||||
target = r.d(reg);
|
||||
target = read<Size>(rd);
|
||||
result = source + target;
|
||||
r.d(reg) = result;
|
||||
write<Size>(rd, result);
|
||||
} else {
|
||||
source = r.d(reg);
|
||||
source = read<Size>(rd);
|
||||
target = read<Size>(ea);
|
||||
result = source + target;
|
||||
write<Size>(ea, result);
|
||||
|
@ -82,95 +72,86 @@ template<uint Size> auto M68K::instructionADD(uint3 reg, uint1 direction, EA ea)
|
|||
r.x = r.c;
|
||||
}
|
||||
|
||||
auto M68K::instructionANDI(uint8 ea) -> void {
|
||||
auto result = modify(ea, readPC(ea), [&](auto x, auto y) -> uint32 {
|
||||
return x & y;
|
||||
});
|
||||
template<uint Size> auto M68K::instructionANDI(EA 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(ea, result);
|
||||
r.n = negative(ea, result);
|
||||
r.z = zero<Size>(result);
|
||||
r.n = negative<Size>(result);
|
||||
}
|
||||
|
||||
auto M68K::instructionBCC(uint4 condition, uint8 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 += displacement;
|
||||
auto extension = readPC();
|
||||
if(condition == 1); //push<Long>(r.pc);
|
||||
r.pc -= 2;
|
||||
if(!testCondition(condition == 1 ? (uint4)0 : condition)) return;
|
||||
r.pc += displacement ? sign<Byte>(displacement) : sign<Word>(extension);
|
||||
}
|
||||
|
||||
auto M68K::instructionLEA(uint3 target, uint8 ea) -> void {
|
||||
r.a(target) = address(ea);
|
||||
auto M68K::instructionLEA(Register ra, EA ea) -> void {
|
||||
write<Long>(ra, fetch<Long>(ea));
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE(uint8 to, uint8 from) -> void {
|
||||
auto data = read(from);
|
||||
write(to, data);
|
||||
template<uint Size> auto M68K::instructionMOVE(EA to, EA from) -> void {
|
||||
auto data = read<Size>(from);
|
||||
write<Size>(to, data);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = zero(from, data);
|
||||
r.n = negative(from, data);
|
||||
r.z = zero<Size>(data);
|
||||
r.n = negative<Size>(data);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEA(uint3 to, uint8 from) -> void {
|
||||
auto data = read(from);
|
||||
if(from & 1) data = (int16)data;
|
||||
r.a(to) = data;
|
||||
template<uint Size> auto M68K::instructionMOVEA(Register ra, EA ea) -> void {
|
||||
auto data = read<Size>(ea);
|
||||
if(Size == Word) data = (int16)data;
|
||||
write<Size>(ra, data);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEM(uint1 direction, uint8 ea) -> void {
|
||||
template<uint Size> auto M68K::instructionMOVEM(uint1 direction, EA ea) -> void {
|
||||
auto list = readPC();
|
||||
auto addr = address(ea);
|
||||
auto addr = fetch<Size>(ea);
|
||||
|
||||
for(uint n : range(8)) {
|
||||
if(list.bit(0 + n)) {
|
||||
r.d(n) = readAbsolute(ea, addr);
|
||||
addr += 2 + (ea & 2);
|
||||
for(uint rn : range(16)) {
|
||||
if(list.bit(rn)) {
|
||||
write<Size>(Register{rn}, read<Size>(addr));
|
||||
addr += Size == Long ? 4 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint n : range(8)) {
|
||||
if(list.bit(8 + n)) {
|
||||
r.a(n) = readAbsolute(ea, addr);
|
||||
addr += 2 + (ea & 2);
|
||||
}
|
||||
}
|
||||
|
||||
flush(ea, addr);
|
||||
flush<Size>(ea, addr);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVEQ(uint3 target, uint8 immediate) -> void {
|
||||
r.d(target) = immediate;
|
||||
auto M68K::instructionMOVEQ(Register rd, uint8 immediate) -> void {
|
||||
write<Byte>(rd, immediate);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = immediate == 0;
|
||||
r.n = negative(Byte, immediate);
|
||||
r.z = zero<Byte>(immediate);
|
||||
r.n = negative<Byte>(immediate);
|
||||
}
|
||||
|
||||
auto M68K::instructionMOVE_USP(uint1 direction, uint3 reg) -> void {
|
||||
auto M68K::instructionMOVE_USP(uint1 direction, Register ra) -> void {
|
||||
if(!r.s) trap(); //todo: proper trap
|
||||
if(direction == 0) {
|
||||
r.usp = r.a(reg);
|
||||
r.usp = read<Long>(ra);
|
||||
} else {
|
||||
r.a(reg) = r.usp;
|
||||
write<Long>(ra, r.usp);
|
||||
}
|
||||
}
|
||||
|
||||
auto M68K::instructionNOP() -> void {
|
||||
}
|
||||
|
||||
auto M68K::instructionTST(uint8 ea) -> void {
|
||||
auto data = read(ea);
|
||||
template<uint Size> auto M68K::instructionTST(EA ea) -> void {
|
||||
auto data = read<Size>(ea);
|
||||
|
||||
r.c = 0;
|
||||
r.v = 0;
|
||||
r.z = zero(ea, data);
|
||||
r.n = negative(ea, data);
|
||||
r.z = zero<Size>(data);
|
||||
r.n = negative<Size>(data);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ auto M68K::power() -> void {
|
|||
auto M68K::reset() -> void {
|
||||
instructionsExecuted = 0;
|
||||
|
||||
for(uint n : range(8)) r.d(n) = 0;
|
||||
for(uint n : range(7)) r.a(n) = 0;
|
||||
for(uint rn : range(15)) write<Long>(Register{rn}, 0);
|
||||
r.ssp = 0;
|
||||
r.usp = 0;
|
||||
r.pc = 0;
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Processor {
|
|||
|
||||
struct M68K {
|
||||
enum : uint { Byte, Word, Long };
|
||||
enum : bool { NoUpdate = 0 };
|
||||
|
||||
M68K();
|
||||
|
||||
|
@ -16,32 +17,39 @@ struct M68K {
|
|||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
//registers.cpp
|
||||
struct Register {
|
||||
Register(uint number) : number(number) {}
|
||||
|
||||
uint4 number;
|
||||
};
|
||||
|
||||
template<uint Size = Long> auto read(Register reg) -> uint32;
|
||||
template<uint Size = Long> auto write(Register reg, uint32 value) -> void;
|
||||
|
||||
//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;
|
||||
template<uint Size> auto write(uint32 addr, uint32 data) -> void;
|
||||
template<uint Size = Word> auto readPC() -> uint32;
|
||||
|
||||
//ea.cpp
|
||||
struct EA {
|
||||
EA(uint3 mode, uint3 reg) : mode(mode), reg(reg) {
|
||||
if(mode == 7) mode += reg; //optimization: convert modes {7; 0-4} to {8-11}
|
||||
EA(uint mode_, uint reg_) : mode(mode_), reg(reg_) {
|
||||
if(mode == 7) mode += reg.number; //optimization: convert modes {7; 0-4} to {8-11}
|
||||
if(mode != 0) reg.number += 8; //optimization: linear index to all registers: d0-d7; a0-a7
|
||||
}
|
||||
|
||||
uint4 mode;
|
||||
uint3 reg;
|
||||
Register reg;
|
||||
|
||||
boolean valid;
|
||||
uint32 address;
|
||||
};
|
||||
|
||||
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;
|
||||
template<uint Size> auto fetch(EA& ea) -> uint32;
|
||||
template<uint Size, bool Update = 1> auto read(EA& ea) -> uint32;
|
||||
template<uint Size, bool Update = 1> auto write(EA& ea, uint32 data) -> void;
|
||||
template<uint Size> auto flush(EA& ea, uint32 data) -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto trap() -> void;
|
||||
|
@ -57,29 +65,24 @@ struct M68K {
|
|||
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, 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(uint8 ea) -> void;
|
||||
template<uint Size> auto instructionADD(Register rd, uint1 direction, EA ea) -> void;
|
||||
template<uint Size> auto instructionANDI(EA ea) -> void;
|
||||
auto instructionBCC(uint4 condition, uint8 displacement) -> void;
|
||||
auto instructionLEA(Register ra, EA ea) -> void;
|
||||
template<uint Size> auto instructionMOVE(EA to, EA from) -> void;
|
||||
template<uint Size> auto instructionMOVEA(Register ra, EA ea) -> void;
|
||||
template<uint Size> auto instructionMOVEM(uint1 direction, EA ea) -> void;
|
||||
auto instructionMOVEQ(Register rd, uint8 immediate) -> void;
|
||||
auto instructionMOVE_USP(uint1 direction, Register ra) -> void;
|
||||
auto instructionNOP() -> void;
|
||||
template<uint Size> auto instructionTST(EA ea) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(uint32 pc) -> string;
|
||||
auto disassembleRegisters() -> string;
|
||||
|
||||
struct Registers {
|
||||
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, ssp, usp;
|
||||
uint32 pc;
|
||||
|
@ -106,28 +109,27 @@ struct M68K {
|
|||
|
||||
private:
|
||||
//disassembler.cpp
|
||||
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, 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(uint8 ea) -> string;
|
||||
template<uint Size> auto disassembleADD(Register rd, uint1 direction, EA ea) -> string;
|
||||
template<uint Size> auto disassembleANDI(EA ea) -> string;
|
||||
auto disassembleBCC(uint4 condition, uint8 displacement) -> string;
|
||||
auto disassembleLEA(Register ra, EA ea) -> string;
|
||||
template<uint Size> auto disassembleMOVE(EA to, EA from) -> string;
|
||||
template<uint Size> auto disassembleMOVEA(Register ra, EA ea) -> string;
|
||||
template<uint Size> auto disassembleMOVEM(uint1 direction, EA ea) -> string;
|
||||
auto disassembleMOVEQ(Register rd, uint8 immediate) -> string;
|
||||
auto disassembleMOVE_USP(uint1 direction, Register ra) -> string;
|
||||
auto disassembleNOP() -> string;
|
||||
template<uint Size> auto disassembleTST(EA ea) -> string;
|
||||
|
||||
auto _readByte(uint32 addr) -> uint8;
|
||||
auto _readWord(uint32 addr) -> uint16;
|
||||
auto _readLong(uint32 addr) -> uint32;
|
||||
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;
|
||||
template<uint Size> auto _read(uint32 addr) -> uint32;
|
||||
template<uint Size = Word> auto _readPC() -> uint32;
|
||||
auto _register(Register r) -> string;
|
||||
template<uint Size> auto _immediate() -> string;
|
||||
template<uint Size> auto _address(EA ea) -> string;
|
||||
template<uint Size> auto _read(EA ea) -> string;
|
||||
template<uint Size> auto _write(EA ea) -> string;
|
||||
auto _branch(uint8 displacement) -> string;
|
||||
auto _suffix(uint2 size) -> string;
|
||||
template<uint Size> auto _suffix() -> string;
|
||||
auto _condition(uint4 condition) -> string;
|
||||
|
||||
uint32 _pc;
|
||||
|
|
|
@ -1,24 +1,4 @@
|
|||
auto M68K::readAbsolute(uint2 size, uint32 addr) -> uint32 {
|
||||
step(4);
|
||||
uint32 data = read(size != Byte, addr);
|
||||
if(size != Long) return data;
|
||||
|
||||
step(4);
|
||||
data = data << 16 | read(1, addr + 2);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto M68K::writeAbsolute(uint2 size, uint32 addr, uint32 data) -> void {
|
||||
if(size == Long) {
|
||||
write(1, addr + 0, data >> 16);
|
||||
write(1, addr + 2, data >> 0);
|
||||
} else {
|
||||
write(size != Byte, addr, data >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/*
|
||||
auto M68K::readPC(uint2 size) -> uint32 {
|
||||
step(4);
|
||||
uint32 data = read(size != Byte, r.pc);
|
||||
|
@ -30,8 +10,7 @@ auto M68K::readPC(uint2 size) -> uint32 {
|
|||
r.pc += 2;
|
||||
return data;
|
||||
}
|
||||
|
||||
//
|
||||
*/
|
||||
|
||||
template<> auto M68K::read<Byte>(uint32 addr) -> uint32 {
|
||||
step(4);
|
||||
|
@ -49,3 +28,40 @@ template<> auto M68K::read<Long>(uint32 addr) -> uint32 {
|
|||
step(4);
|
||||
return data | read(1, addr + 2) << 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<> auto M68K::write<Byte>(uint32 addr, uint32 data) -> void {
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Word>(uint32 addr, uint32 data) -> void {
|
||||
}
|
||||
|
||||
template<> auto M68K::write<Long>(uint32 addr, uint32 data) -> void {
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template<> auto M68K::readPC<Byte>() -> uint32 {
|
||||
step(4);
|
||||
uint32 data = read(1, r.pc);
|
||||
r.pc += 2;
|
||||
return (uint8)data;
|
||||
}
|
||||
|
||||
template<> auto M68K::readPC<Word>() -> uint32 {
|
||||
step(4);
|
||||
uint32 data = read(1, r.pc);
|
||||
r.pc += 2;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto M68K::readPC<Long>() -> uint32 {
|
||||
step(4);
|
||||
uint32 data = read(1, r.pc) << 16;
|
||||
r.pc += 2;
|
||||
step(4);
|
||||
data |= read(1, r.pc);
|
||||
r.pc += 2;
|
||||
return data;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,42 @@
|
|||
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;
|
||||
template<uint Size> auto M68K::read(Register reg) -> uint32 {
|
||||
switch(reg.number) {
|
||||
case 0: return clip<Size>(r.d0);
|
||||
case 1: return clip<Size>(r.d1);
|
||||
case 2: return clip<Size>(r.d2);
|
||||
case 3: return clip<Size>(r.d3);
|
||||
case 4: return clip<Size>(r.d4);
|
||||
case 5: return clip<Size>(r.d5);
|
||||
case 6: return clip<Size>(r.d6);
|
||||
case 7: return clip<Size>(r.d7);
|
||||
case 8: return clip<Size>(r.a0);
|
||||
case 9: return clip<Size>(r.a1);
|
||||
case 10: return clip<Size>(r.a2);
|
||||
case 11: return clip<Size>(r.a3);
|
||||
case 12: return clip<Size>(r.a4);
|
||||
case 13: return clip<Size>(r.a5);
|
||||
case 14: return clip<Size>(r.a6);
|
||||
case 15: return r.s ? clip<Size>(r.ssp) : clip<Size>(r.usp);
|
||||
}
|
||||
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;
|
||||
template<uint Size> auto M68K::write(Register reg, uint32 data) -> void {
|
||||
switch(reg.number) {
|
||||
case 0: r.d0 = clip<Size>(data); return;
|
||||
case 1: r.d1 = clip<Size>(data); return;
|
||||
case 2: r.d2 = clip<Size>(data); return;
|
||||
case 3: r.d3 = clip<Size>(data); return;
|
||||
case 4: r.d4 = clip<Size>(data); return;
|
||||
case 5: r.d5 = clip<Size>(data); return;
|
||||
case 6: r.d6 = clip<Size>(data); return;
|
||||
case 7: r.d7 = clip<Size>(data); return;
|
||||
case 8: r.a0 = clip<Size>(data); return;
|
||||
case 9: r.a1 = clip<Size>(data); return;
|
||||
case 10: r.a2 = clip<Size>(data); return;
|
||||
case 11: r.a3 = clip<Size>(data); return;
|
||||
case 12: r.a4 = clip<Size>(data); return;
|
||||
case 13: r.a5 = clip<Size>(data); return;
|
||||
case 14: r.a6 = clip<Size>(data); return;
|
||||
case 15: r.s ? r.ssp = clip<Size>(data) : r.usp = clip<Size>(data); return;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue