mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
76a8ecd32a
commit
1c0ef793fe
|
@ -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/";
|
||||
|
|
|
@ -10,8 +10,8 @@ auto CPU::Enter() -> void {
|
|||
}
|
||||
|
||||
auto CPU::boot() -> void {
|
||||
r.sp = readLong(0);
|
||||
r.pc = readLong(4);
|
||||
r.ssp = readLong(0);
|
||||
r.pc = readLong(4);
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue