Update to v100r04 release.

byuu says:

I now have enough of three instructions implemented to get through the
first four instructions in Sonic the Hedgehog.

But they're far from complete. The very first instruction uses EA
addressing, which is similar to x86's ModRM in terms of how disgustingly
complex it is. And it also accesses Z80 control registers, which obviously
isn't going to do anything yet.

The slow speed was me being stupid again. It's not 7.6MHz per frame,
it's 7.67MHz per second. So yeah, speed is so far acceptable again. But
we'll see how things go as I keep emulating more. The 68K decode is not
pretty at all.
This commit is contained in:
Tim Allen 2016-07-12 20:19:31 +10:00
parent 76a8ecd32a
commit 1c0ef793fe
11 changed files with 276 additions and 75 deletions

View File

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

View File

@ -10,7 +10,7 @@ auto CPU::Enter() -> void {
}
auto CPU::boot() -> void {
r.sp = readLong(0);
r.ssp = readLong(0);
r.pc = readLong(4);
}
@ -20,8 +20,8 @@ auto CPU::main() -> void {
auto CPU::step(uint clocks) -> void {
clock += clocks;
while(clock >= frequency) {
clock -= frequency;
if(clock >= frequency / 60) {
clock -= frequency / 60;
scheduler.exit(Scheduler::Event::Frame);
}
}

View File

@ -10,8 +10,8 @@ Interface::Interface() {
information.manufacturer = "Sega";
information.name = "Mega Drive";
information.width = 1280;
information.height = 480;
information.width = 320; //1280
information.height = 240; // 480
information.overscan = true;
information.aspectRatio = 4.0 / 3.0;
information.resettable = true;

View File

@ -7,8 +7,8 @@ Scheduler scheduler;
auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) {
static uint32 output[1280 * 480] = {0};
Emulator::video.refresh(output, 1280 * sizeof(uint32), 1280, 480);
static uint32 output[320 * 240] = {0};
Emulator::video.refresh(output, 320 * sizeof(uint32), 320, 240);
}
}

View File

@ -0,0 +1,96 @@
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 suffix = [&](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 == 7) {
if(reg == 0) return {"($", hex((int16)readWordPC(), 6L), ")", suffix(size)};
if(reg == 1) return {"($", hex(readLongPC(), 6L), ")", suffix(size)};
}
return "???";
};
string s, op;
s.append(hex(pc, 6L), " ");
auto opcode = readWordPC();
s.append(hex(opcode, 4L), " ");
if(0);
//bcc
else if(opcode >> 12 == 0b0110) {
op = {"b", cc(opcode >> 8), " ", branch(opcode >> 0)};
}
//nop
else if(opcode == 0b0100'1110'0111'0001) {
op = {"nop"};
}
//tst
else if(opcode >> 8 == 0b0100'1010) {
op = {"tst", suffix(opcode >> 6), " ", ea(opcode >> 6, opcode >> 3, opcode >> 0)};
}
else {
op = {"???"};
}
s.append(op);
return s;
}

View File

@ -0,0 +1,25 @@
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;
return 0;
}
auto M68K::readEA(uint2 size, uint3 mode, uint3 reg) -> uint32 {
if(mode == 7) {
if(reg == 0) {
uint32 addr = (int16)readWordPC();
return readAbsolute(size, addr);
}
if(reg == 1) {
uint32 addr = readLongPC();
return readAbsolute(size, addr);
}
}
return 0;
}
auto M68K::writeEA(uint2 size, uint3 mode, uint3 reg, uint32 data) -> void {
}

View File

@ -0,0 +1,34 @@
auto M68K::trap() -> void {
instructionsExecuted--;
r.pc -= 2;
print("[M68K] unimplemented instruction: ", hex(r.pc, 6L), " = ", hex(opcode, 4L), "\n");
print("[M68K] instructions executed: ", instructionsExecuted, "\n");
while(true) step(5);
}
auto M68K::instruction() -> void {
instructionsExecuted++;
print(disassemble(r.pc), "\n");
opcode = readWordPC();
//bcc
//0110 cccc dddd dddd
if(opcode >> 12 == 0b0110) {
return instructionBCC(opcode >> 8, opcode >> 0);
}
//nop
//0100 1110 0111 0001
if(opcode == 0b0100'1110'0111'0001) {
return instructionNOP();
}
//tst
//0100 1010 ssmm mrrr
if(opcode >> 8 == 0b0100'1010) {
return instructionTST(opcode >> 6, opcode >> 3, opcode >> 0);
}
trap();
}

View File

@ -0,0 +1,47 @@
auto M68K::testCondition(uint4 condition) -> bool {
switch(condition) {
case 0: return true; //RA
case 1: return false; //NV,SR
case 2: return !r.c && !r.z; //HI
case 3: return r.c || r.z; //LS
case 4: return !r.c; //CC,HS
case 5: return r.c; //CS,LO
case 6: return !r.z; //NE
case 7: return r.z; //EQ
case 8: return !r.v; //VC
case 9: return r.v; //VS
case 10: return !r.n; //PL
case 11: return r.n; //MI
case 12: return r.n == r.v; //GE
case 13: return r.n != r.v; //LT
case 14: return r.n == r.v && !r.z; //GT
case 15: return r.n != r.v || r.z; //LE
}
unreachable;
}
//
auto M68K::instructionBCC(uint4 condition, uint8 displacementByte) -> void {
uint16 displacementWord = readWordPC();
if(displacementByte) r.pc -= 2;
if(condition == 1) {
condition = 0;
//pushLong(r.pc);
}
if(testCondition(condition)) {
r.pc += displacementByte ? (int8_t)displacementByte : ((int16_t)displacementWord - 2);
}
}
auto M68K::instructionNOP() -> void {
}
auto M68K::instructionTST(uint2 size, uint3 rdMode, uint3 rdReg) -> void {
auto data = readEA(size, rdMode, rdReg);
r.c = 0;
r.v = 0;
r.z = data == 0;
r.n = signExtend(size, data) < 0;
}

View File

@ -4,34 +4,10 @@
namespace Processor {
#include "memory.cpp"
auto M68K::instruction() -> void {
instructionsExecuted++;
auto opcode = readWord(r.pc);
r.pc += 2;
//nop
if(opcode == 0x4e71) {
step(5);
return;
}
//bra disp
if((opcode & 0xff00) == 0x6000) {
int displacement = (int8)opcode;
if(!displacement) displacement = (int16)readWord(r.pc);
r.pc += displacement;
step(12);
return;
}
instructionsExecuted--;
r.pc -= 2;
print("[M68K] unimplemented instruction: ", hex(r.pc, 6L), " = ", hex(opcode, 4L), "\n");
print("[M68K] executed ", instructionsExecuted, " instructions\n");
while(true) step(5);
}
#include "ea.cpp"
#include "instruction.cpp"
#include "instructions.cpp"
#include "disassembler.cpp"
auto M68K::power() -> void {
}
@ -39,10 +15,10 @@ auto M68K::power() -> void {
auto M68K::reset() -> void {
instructionsExecuted = 0;
for(auto& n : r.d) n = 0;
for(auto& n : r.a) n = 0;
r.pc = 0x000200;
r.ccr.value = 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;
r.pc = 0;
r.ccr = 0;
}
}

View File

@ -11,35 +11,53 @@ struct M68K {
auto power() -> void;
auto reset() -> void;
auto instruction() -> void;
//memory.cpp
auto readByte(uint32 addr) -> uint8;
auto readWord(uint32 addr) -> uint16;
auto readLong(uint32 addr) -> uint32;
auto readQuad(uint32 addr) -> uint64;
auto readWordPC() -> uint16;
auto readLongPC() -> uint32;
auto readAbsolute(uint2 size, uint32 addr) -> uint32;
//ea.cpp
auto signExtend(uint2 size, uint32 data) -> int32;
auto readEA(uint2 size, uint3 mode, uint3 reg) -> uint32;
auto writeEA(uint2 size, uint3 mode, uint3 reg, uint32 data) -> void;
//instruction.cpp
auto trap() -> void;
auto instruction() -> void;
//instructions.cpp
auto testCondition(uint4 condition) -> bool;
auto instructionBCC(uint4 condition, uint8 displacementByte) -> void;
auto instructionNOP() -> void;
auto instructionTST(uint2 size, uint3 rdMode, uint3 rdReg) -> void;
//disassembler.cpp
auto disassemble(uint32 pc) -> string;
struct Registers {
//todo: this is almost certainly UB or IB due to alignment rules ...
union {
uint32_t d[8];
struct { uint32_t d0, d1, d2, d3, d4, d5, d6, d7; };
};
union {
uint32_t a[8];
struct { uint32_t a0, a1, a2, a3, a4, a5, a6; union { uint32_t a7, sp; }; };
};
uint32 d0, d1, d2, d3, d4, d5, d6, d7;
uint32 a0, a1, a2, a3, a4, a5, a6, usp, ssp;
uint32 pc;
union CCR {
uint8_t value = 0;
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
} ccr;
};
Registers() : ccr(0) {}
} r;
uint16 opcode = 0;
uint instructionsExecuted = 0;
};

View File

@ -1,32 +1,37 @@
auto M68K::readByte(uint32 addr) -> uint8 {
step(4);
return read(addr);
}
auto M68K::readWord(uint32 addr) -> uint16 {
uint16 data;
data |= read(addr + 0) << 8;
data |= read(addr + 1) << 0;
return data;
step(4);
uint16 data = read(addr + 0) << 8;
return data |= read(addr + 1) << 0;
}
auto M68K::readLong(uint32 addr) -> uint32 {
uint32 data;
data |= read(addr + 0) << 24;
data |= read(addr + 1) << 16;
data |= read(addr + 2) << 8;
data |= read(addr + 3) << 0;
uint32 data = readWord(addr + 0) << 16;
return data |= readWord(addr + 2) << 0;
}
//
auto M68K::readWordPC() -> uint16 {
uint16 data = readWord(r.pc);
r.pc += 2;
return data;
}
auto M68K::readQuad(uint32 addr) -> uint64 {
uint64 data;
data |= (uint64)read(addr + 0) << 56;
data |= (uint64)read(addr + 1) << 48;
data |= (uint64)read(addr + 2) << 40;
data |= (uint64)read(addr + 3) << 32;
data |= (uint64)read(addr + 4) << 24;
data |= (uint64)read(addr + 5) << 16;
data |= (uint64)read(addr + 6) << 8;
data |= (uint64)read(addr + 7) << 0;
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;
}