Update to v087r08 release.

byuu says:

Added some more ARM opcodes, hooked up MMIO. Bind it with mmio[(addr
000-3ff)] = this; inside CPU/PPU/APU, goes to read(), write().
Also moved the Hitachi HG51B core to processor/, and split it apart from
the snes/chip/hitachidsp implementation.
This one actually worked really well. Very clean split between MMIO/DMA
and the processor core. I may move a more generic DMA function inside
the core, not sure yet.

I still believe the HG51B169 to be a variant of the HG51BS family, but
given they're meant to be incredibly flexible microcontrollers, it's
possible that each variant gets its own instruction set.
So, who knows. We'll worry about it if we ever find another HG51B DSP,
I guess.

GBA BIOS is constantly reading from 04000300, but it never writes. If
I return prng()&1, I can get it to proceed until it hits a bad opcode
(stc opcode, which the GBA lacks a coprocessor so ... bad codepath.)
Without it, it just reads that register forever and keeps resetting the
system, or something ...

I guess we're going to have to try and get ARMwrestler working, because
the BIOS seems to need too much emulation code to do anything at all.
This commit is contained in:
Tim Allen 2012-03-24 18:52:36 +11:00
parent ec939065bd
commit 395e5a5639
36 changed files with 498 additions and 255 deletions

View File

@ -14,7 +14,7 @@ target := ui
c := $(compiler) -std=gnu99
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -I. -march=native -O3 -fomit-frame-pointer
link :=
link := -s
objects := libco
# profile-guided optimization mode
@ -30,11 +30,11 @@ endif
# platform
ifeq ($(platform),x)
link += -s -ldl -lX11 -lXext
link += -ldl -lX11 -lXext
else ifeq ($(platform),osx)
else ifeq ($(platform),win)
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
else
unknown_platform: help;

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.07";
static const char Version[] = "087.08";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

@ -18,6 +18,13 @@ void APU::step(unsigned clocks) {
if(clock >= 0) co_switch(cpu.thread);
}
uint32 APU::read(uint32 addr, uint32 size) {
return 0u;
}
void APU::write(uint32 addr, uint32 size, uint32 word) {
}
void APU::power() {
create(APU::Enter, 16777216);
}

View File

@ -1,8 +1,10 @@
struct APU : Thread {
struct APU : Thread, Memory {
static void Enter();
void enter();
void step(unsigned clocks);
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
void power();
};

View File

@ -9,6 +9,9 @@ bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
cartridge.rom.data = new uint8[cartridge.rom.size = size];
memcpy(cartridge.rom.data, data, size);
if(cartridge.ram.data) delete[] cartridge.ram.data;
cartridge.ram.data = new uint8[cartridge.ram.size = 64 * 1024]();
sha256 = nall::sha256(cartridge.rom.data, cartridge.rom.size);
return loaded = true;

View File

@ -9,6 +9,8 @@ void CPU::Enter() { cpu.enter(); }
void CPU::enter() {
while(true) {
if(exception) {
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
: disassemble_arm_instruction(pipeline.execute.address), "\n");
print(disassemble_registers(), "\n");
while(true) step(frequency);
}
@ -38,13 +40,26 @@ void CPU::power() {
create(CPU::Enter, 16777216);
ARM::power();
for(unsigned n = 0; n < iram.size; n++) iram.data[n] = 0;
for(unsigned n = 0; n < eram.size; n++) eram.data[n] = 0;
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
bus.mmio[0x0300] = this;
bus.mmio[0x0301] = this;
bus.mmio[0x0302] = this;
bus.mmio[0x0303] = this;
}
uint32 CPU::read(uint32 addr, uint32 size) {
if(addr == 0x04000300) return prng() & 1;
return 0u;
}
void CPU::write(uint32 addr, uint32 size, uint32 word) {
}
CPU::CPU() {
iram.data = new uint8[iram.size = 32 * 1024];
eram.data = new uint8[eram.size = 256 * 1024];
iwram.data = new uint8[iwram.size = 32 * 1024];
ewram.data = new uint8[ewram.size = 256 * 1024];
}
}

View File

@ -1,6 +1,6 @@
struct CPU : Processor::ARM, Thread {
StaticMemory iram;
StaticMemory eram;
struct CPU : Processor::ARM, Thread, Memory {
StaticMemory iwram;
StaticMemory ewram;
static void Enter();
void enter();
@ -10,6 +10,9 @@ struct CPU : Processor::ARM, Thread {
void power();
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
CPU();
};

View File

@ -4,6 +4,13 @@ namespace GBA {
Bus bus;
struct UnmappedMemory : Memory {
uint32 read(uint32 addr, uint32 size) { return 0u; }
void write(uint32 addr, uint32 size, uint32 word) {}
};
static UnmappedMemory unmappedMemory;
uint32 StaticMemory::read(uint32 addr, uint32 size) {
uint32 word = 0;
@ -63,9 +70,9 @@ uint32 Bus::read(uint32 addr, uint32 size) {
switch(addr & 0x0f000000) {
case 0x00000000: return system.bios.read(addr & 0x3fff, size);
case 0x01000000: return system.bios.read(addr & 0x3fff, size);
case 0x02000000: return cpu.eram.read(addr & 0x3ffff, size);
case 0x03000000: return cpu.iram.read(addr & 0x7fff, size);
case 0x04000000: return 0u; //MMIO [0x400]
case 0x02000000: return cpu.ewram.read(addr & 0x3ffff, size);
case 0x03000000: return cpu.iwram.read(addr & 0x7fff, size);
case 0x04000000: return mmio[addr & 0x3ff]->read(addr, size);
case 0x05000000: return ppu.pram.read(addr & 0x3ff, size);
case 0x06000000: return ppu.vram.read(addr & 0x10000 ? (0x10000 + (addr & 0x7fff)) : (addr & 0xffff), size);
case 0x07000000: return ppu.oam.read(addr & 0x3ff, size);
@ -84,9 +91,9 @@ void Bus::write(uint32 addr, uint32 size, uint32 word) {
switch(addr & 0x0f000000) {
case 0x00000000: return;
case 0x01000000: return;
case 0x02000000: return cpu.eram.write(addr & 0x3ffff, size, word);
case 0x03000000: return cpu.iram.write(addr & 0x7fff, size, word);
case 0x04000000: return; //MMIO [0x400]
case 0x02000000: return cpu.ewram.write(addr & 0x3ffff, size, word);
case 0x03000000: return cpu.iwram.write(addr & 0x7fff, size, word);
case 0x04000000: return mmio[addr & 0x3ff]->write(addr, size, word);
case 0x05000000: return ppu.pram.write(addr & 0x3ff, size, word);
case 0x06000000: return ppu.vram.write(addr & 0x10000 ? (0x10000 + (addr & 0x7fff)) : (addr & 0xffff), size, word);
case 0x07000000: return ppu.oam.write(addr & 0x3ff, size, word);
@ -101,4 +108,8 @@ void Bus::write(uint32 addr, uint32 size, uint32 word) {
}
}
void Bus::power() {
for(unsigned n = 0; n < 0x400; n++) mmio[n] = &unmappedMemory;
}
}

View File

@ -3,7 +3,7 @@ struct Memory {
virtual void write(uint32 addr, uint32 size, uint32 word) = 0;
};
struct StaticMemory {
struct StaticMemory : Memory {
uint8_t *data;
unsigned size;
@ -14,8 +14,11 @@ struct StaticMemory {
};
struct Bus : Memory {
Memory *mmio[0x400];
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
void power();
};
extern Bus bus;

View File

@ -56,6 +56,13 @@ void PPU::frame() {
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
uint32 PPU::read(uint32 addr, uint32 size) {
return 0u;
}
void PPU::write(uint32 addr, uint32 size, uint32 word) {
}
PPU::PPU() {
vram.data = new uint8[vram.size = 96 * 1024];
oam.data = new uint8[oam.size = 1024];

View File

@ -1,4 +1,4 @@
struct PPU : Thread {
struct PPU : Thread, Memory {
StaticMemory vram;
StaticMemory oam;
StaticMemory pram;
@ -10,6 +10,9 @@ struct PPU : Thread {
void power();
void frame();
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
PPU();
};

View File

@ -24,6 +24,7 @@ void System::term() {
}
void System::power() {
bus.power();
cpu.power();
ppu.power();
apu.power();

View File

@ -1,6 +1,8 @@
processor_objects :=
processor_objects += $(if $(findstring arm,$(processors)),processor-arm)
processor_objects += $(if $(findstring hg51b,$(processors)),processor-hg51b)
objects += $(processor_objects)
processor := processor
obj/processor-arm.o: $(processor)/arm/arm.cpp $(call rwildcard,$(processor)/arm)
obj/processor-arm.o: $(processor)/arm/arm.cpp $(call rwildcard,$(processor)/arm)
obj/processor-hg51b.o: $(processor)/hg51b/hg51b.cpp $(call rwildcard,$(processor)/hg51b)

View File

@ -10,6 +10,7 @@ namespace Processor {
void ARM::power() {
processor.power();
vector(0x00000000, Processor::Mode::SVC);
pipeline.reload = true;
exception = false;
r(15).modify = [&] {
@ -18,6 +19,17 @@ void ARM::power() {
};
}
void ARM::vector(uint32 addr, Processor::Mode mode) {
auto psr = cpsr();
processor.setMode(mode);
spsr() = psr;
cpsr().i = 1;
cpsr().f = mode == Processor::Mode::FIQ;
cpsr().t = 0;
r(14) = pipeline.decode.address;
r(15) = addr;
}
void ARM::serialize(serializer &s) {
}

View File

@ -3,6 +3,8 @@
namespace Processor {
//ARMv3, ARMv4TM
struct ARM {
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
#include "registers.hpp"
@ -14,6 +16,8 @@ struct ARM {
virtual void bus_write(uint32 addr, uint32 size, uint32 word) = 0;
void power();
void vector(uint32 addr, Processor::Mode mode);
void serialize(serializer&);
};

View File

@ -1,4 +1,6 @@
string ARM::disassemble_arm_opcode(uint32 pc) {
#ifdef PROCESSOR_ARM_HPP
string ARM::disassemble_arm_instruction(uint32 pc) {
static string conditions[] = {
"eq", "ne", "cs", "cc",
"mi", "pl", "vs", "vc",
@ -65,6 +67,21 @@ string ARM::disassemble_arm_opcode(uint32 pc) {
return output;
}
//memory_swap()
//swp{condition}{b} rd,rm,[rn]
if((instruction & 0x0fb000f0) == 0x01000090) {
uint4 condition = instruction >> 28;
uint1 byte = instruction >> 22;
uint4 rn = instruction >> 16;
uint4 rd = instruction >> 12;
uint4 rm = instruction;
output.append("swp", conditions[condition], byte ? "b " : " ");
output.append(registers[rd], ",", registers[rm], "[", registers[rn], "]");
return output;
}
//move_to_status_from_register()
//msr{condition} (c,s)psr:{fields},rm
if((instruction & 0x0fb000f0) == 0x01200000) {
@ -98,6 +115,30 @@ string ARM::disassemble_arm_opcode(uint32 pc) {
return output;
}
//move_to_status_from_immediate()
//msr{condition} (c,s)psr:{fields},#immediate
if((instruction & 0x0fb00000) == 0x03200000) {
uint4 condition = instruction >> 28;
uint1 psr = instruction >> 22;
uint4 field = instruction >> 16;
uint4 rotate = instruction >> 8;
uint8 immediate = instruction;
uint32 rm = (immediate >> (rotate * 2)) | (immediate << (32 - (rotate * 2)));
output.append("msr", conditions[condition], " ");
output.append(psr ? "spsr:" : "cpsr:");
output.append(
field & 1 ? "c" : "",
field & 2 ? "x" : "",
field & 4 ? "s" : "",
field & 8 ? "f" : ""
);
output.append(",#0x", hex<8>(immediate));
return output;
}
//data_immediate_shift()
//{opcode}{condition}{s} rd,rm {shift} #immediate
//{opcode}{condition} rn,rm {shift} #immediate
@ -265,11 +306,21 @@ string ARM::disassemble_arm_opcode(uint32 pc) {
return output;
}
//software_interrupt()
//swi #immediate
if((instruction & 0x0f000000) == 0x0f000000) {
uint24 immediate = instruction;
output.append("swi #0x", hex<6>(immediate));
return output;
}
output.append("???");
return output;
}
string ARM::disassemble_thumb_opcode(uint32 pc) {
string ARM::disassemble_thumb_instruction(uint32 pc) {
static string conditions[] = {
"eq", "ne", "cs", "cc",
"mi", "pl", "vs", "vc",
@ -602,3 +653,5 @@ string ARM::disassemble_registers() {
output.append( spsr().n ? "N" : "n", spsr().z ? "Z" : "z", spsr().c ? "C" : "c", spsr().v ? "V" : "v");
return output;
}
#endif

View File

@ -1,3 +1,3 @@
string disassemble_arm_opcode(uint32 pc);
string disassemble_thumb_opcode(uint32 pc);
string disassemble_arm_instruction(uint32 pc);
string disassemble_thumb_instruction(uint32 pc);
string disassemble_registers();

View File

@ -1,3 +1,5 @@
#ifdef PROCESSOR_ARM_HPP
void ARM::arm_step() {
if(pipeline.reload) {
pipeline.reload = false;
@ -20,13 +22,17 @@ void ARM::arm_step() {
step(2);
//print(disassemble_registers(), "\n");
//print(disassemble_arm_opcode(pipeline.execute.address), "\n");
//print(disassemble_arm_instruction(pipeline.execute.address), "\n");
if(arm_condition() == false) return;
if((instruction() & 0x0fc000f0) == 0x00000090) { arm_op_multiply(); return; }
if((instruction() & 0x0fb000f0) == 0x01000000) { arm_op_move_to_register_from_status(); return; }
if((instruction() & 0x0fb000f0) == 0x01000090) { arm_op_memory_swap(); return; }
if((instruction() & 0x0fb000f0) == 0x01200000) { arm_op_move_to_status_from_register(); return; }
if((instruction() & 0x0ff000f0) == 0x01200010) { arm_op_branch_exchange_register(); return; }
if((instruction() & 0x0ff000f0) == 0x01200010) { arm_op_branch_exchange_register(); return; } //ARMv4
if((instruction() & 0x0fb00000) == 0x03200000) { arm_op_move_to_status_from_immediate(); return; }
if((instruction() & 0x0e000010) == 0x00000000) { arm_op_data_immediate_shift(); return; }
if((instruction() & 0x0e000090) == 0x00000010) { arm_op_data_register_shift(); return; }
if((instruction() & 0x0e000000) == 0x02000000) { arm_op_data_immediate(); return; }
@ -34,6 +40,7 @@ void ARM::arm_step() {
if((instruction() & 0x0e000010) == 0x06000000) { arm_op_move_register_offset(); return; }
if((instruction() & 0x0e000000) == 0x08000000) { arm_op_move_multiple(); return; }
if((instruction() & 0x0e000000) == 0x0a000000) { arm_op_branch(); return; }
if((instruction() & 0x0f000000) == 0x0f000000) { arm_op_software_interrupt(); return; }
exception = true;
}
@ -110,6 +117,35 @@ void ARM::arm_opcode(uint32 rm) {
}
}
void ARM::arm_move_to_status(uint32 rm) {
uint1 source = instruction() >> 22;
uint4 field = instruction() >> 16;
if(source == 1) {
if(mode() == Processor::Mode::USR) return;
if(mode() == Processor::Mode::SYS) return;
}
PSR &psr = source ? spsr() : cpsr();
if(field & 1) {
if(source == 1 || (Processor::Mode)cpsr().m != Processor::Mode::USR) {
psr.i = rm & 0x00000080;
psr.f = rm & 0x00000040;
psr.t = rm & 0x00000020;
psr.m = rm & 0x0000001f;
if(source == 0) processor.setMode((Processor::Mode)psr.m);
}
}
if(field & 8) {
psr.n = rm & 0x80000000;
psr.z = rm & 0x40000000;
psr.c = rm & 0x20000000;
psr.v = rm & 0x10000000;
}
}
//logical shift left
void ARM::lsl(bool &c, uint32 &rm, uint32 rs) {
while(rs--) {
@ -167,23 +203,35 @@ void ARM::arm_op_multiply() {
uint4 s = instruction() >> 8;
uint4 m = instruction() >> 0;
//Booth's algorithm: two bit steps
uint32 temp = r(s);
while(temp) {
temp >>= 2;
step(1);
}
r(d) = r(m) * r(s);
auto &rd = r(d);
uint32 rs = r(s);
auto &rm = r(m);
if(accumulate) {
rd = accumulate ? r(n) : 0u;
step(1);
//Modified Booth Encoding
bool carry = 0;
unsigned place = 0;
do {
step(1);
r(d) += r(n);
}
signed factor = (int2)rs + carry;
if(factor == -2) rd -= rm << (place + 1);
if(factor == -1) rd -= rm << (place + 0);
if(factor == +1) rd += rm << (place + 0);
if(factor == +2) rd += rm << (place + 1);
carry = rs & 2;
place += 2;
rs >>= 2;
} while(rs + carry && place < 32);
if(save) {
cpsr().n = r(d) >> 31;
cpsr().z = r(d) == 0;
cpsr().c = 0; //undefined
cpsr().c = carry;
}
}
@ -204,6 +252,24 @@ void ARM::arm_op_move_to_register_from_status() {
r(d) = source ? spsr() : cpsr();
}
//swp{condition}{b} rd,rm,[rn]
//cccc 0001 0b00 nnnn dddd ---- 1001 mmmm
//c = condition
//b = byte (0 = word)
//n = rn
//d = rd
//m = rm
void ARM::arm_op_memory_swap() {
uint1 byte = instruction() >> 22;
uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12;
uint4 m = instruction();
uint32 word = bus_read(r(n), byte ? Byte : Word);
bus_write(r(n), byte ? Byte : Word, r(m));
r(d) = word;
}
//msr{condition} (c,s)psr:{fields},rm
//cccc 0001 0r10 ffff ++++ ---- 0000 mmmm
//c = condition
@ -211,35 +277,9 @@ void ARM::arm_op_move_to_register_from_status() {
//f = field mask
//m = rm
void ARM::arm_op_move_to_status_from_register() {
uint1 source = instruction() >> 22;
uint4 field = instruction() >> 16;
uint4 m = instruction();
if(source) {
if(mode() == Processor::Mode::USR) return;
if(mode() == Processor::Mode::SYS) return;
}
PSR &psr = source ? spsr() : cpsr();
uint32 rm = r(m);
if(field & 1) {
psr.i = rm & 0x00000080;
psr.f = rm & 0x00000040;
psr.t = rm & 0x00000020;
psr.m = rm & 0x0000001f;
if(source == 0) {
processor.setMode((Processor::Mode)psr.m);
}
}
if(field & 8) {
psr.n = rm & 0x80000000;
psr.z = rm & 0x40000000;
psr.c = rm & 0x20000000;
psr.v = rm & 0x10000000;
}
arm_move_to_status(r(m));
}
//bx{condition} rm
@ -253,6 +293,24 @@ void ARM::arm_op_branch_exchange_register() {
cpsr().t = r(m) & 1;
}
//msr{condition} (c,s)psr:{fields},#immediate
//cccc 0011 0r10 ffff ++++ rrrr iiii iiii
//c = condition
//r = SPSR (0 = CPSR)
//f = field mask
//r = rotate
//i = immediate
void ARM::arm_op_move_to_status_from_immediate() {
uint4 rotate = instruction() >> 8;
uint8 immediate = instruction();
uint32 rm = immediate;
bool c = cpsr().c;
if(rotate) ror(c, rm, 2 * rotate);
arm_move_to_status(rm);
}
//{opcode}{condition}{s} rd,rm {shift} #immediate
//{opcode}{condition} rn,rm {shift} #immediate
//{opcode}{condition}{s} rd,rn,rm {shift} #immediate
@ -489,3 +547,15 @@ void ARM::arm_op_branch() {
if(link) r(14) = r(15) - 4;
r(15) += displacement * 4;
}
//swi #immediate
//cccc 1111 iiii iiii iiii iiii iiii iiii
//c = condition
//i = immediate
void ARM::arm_op_software_interrupt() {
uint24 immediate = instruction();
vector(0x00000008, Processor::Mode::SVC);
}
#endif

View File

@ -2,6 +2,7 @@ void arm_step();
bool arm_condition();
void arm_opcode(uint32 rm);
void arm_move_to_status(uint32 rm);
void lsl(bool &c, uint32 &rm, uint32 rs);
void lsr(bool &c, uint32 &rm, uint32 rs);
@ -11,8 +12,10 @@ void rrx(bool &c, uint32 &rm);
void arm_op_multiply();
void arm_op_move_to_register_from_status();
void arm_op_memory_swap();
void arm_op_move_to_status_from_register();
void arm_op_branch_exchange_register();
void arm_op_move_to_status_from_immediate();
void arm_op_data_immediate_shift();
void arm_op_data_register_shift();
void arm_op_data_immediate();
@ -20,3 +23,4 @@ void arm_op_move_immediate_offset();
void arm_op_move_register_offset();
void arm_op_move_multiple();
void arm_op_branch();
void arm_op_software_interrupt();

View File

@ -1,3 +1,5 @@
#ifdef PROCESSOR_ARM_HPP
void ARM::thumb_step() {
if(pipeline.reload) {
pipeline.reload = false;
@ -20,7 +22,7 @@ void ARM::thumb_step() {
step(1);
//print(disassemble_registers(), "\n");
//print(disassemble_thumb_opcode(pipeline.execute.address), "\n");
//print(disassemble_thumb_instruction(pipeline.execute.address), "\n");
if((instruction() & 0xfc00) == 0x1800) { thumb_op_adjust_register(); return; }
if((instruction() & 0xfc00) == 0x1c00) { thumb_op_adjust_immediate(); return; }
@ -483,13 +485,7 @@ void ARM::thumb_op_move_multiple() {
void ARM::thumb_op_software_interrupt() {
uint8 immediate = instruction();
auto psr = cpsr();
processor.setMode(Processor::Mode::SVC);
r(14) = r(15) - 2;
spsr() = psr;
cpsr().t = 0;
cpsr().i = 1;
r(15) = 0x00000008;
vector(0x00000008, Processor::Mode::SVC);
}
//b{condition}
@ -532,3 +528,5 @@ void ARM::thumb_op_branch_long_suffix() {
r(14) = r(15) | 1;
r(15) += displacement * 2;
}
#endif

View File

@ -1,3 +1,5 @@
#ifdef PROCESSOR_ARM_HPP
void ARM::Processor::power() {
r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = 0;
usr.r8 = usr.r9 = usr.r10 = usr.r11 = usr.r12 = usr.sp = usr.lr = 0;
@ -26,8 +28,6 @@ void ARM::Processor::power() {
r[7] = &r7;
r[15] = &pc;
setMode(Mode::SYS);
}
void ARM::Processor::setMode(Mode mode) {
@ -56,3 +56,5 @@ void ARM::Processor::setMode(Mode mode) {
default: r[13] = &usr.sp; r[14] = &usr.lr; spsr = nullptr; break;
}
}
#endif

27
bsnes/processor/hg51b/hg51b.cpp Executable file
View File

@ -0,0 +1,27 @@
#include <processor/processor.hpp>
#include "hg51b.hpp"
namespace Processor {
#include "registers.cpp"
#include "instructions.cpp"
#include "serialization.cpp"
void HG51B::exec(uint24 addr) {
if(regs.halt) return;
addr = addr + regs.pc * 2;
opcode = bus_read(addr++) << 0;
opcode |= bus_read(addr++) << 8;
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff);
instruction();
}
void HG51B::power() {
regs.halt = true;
regs.n = 0;
regs.z = 0;
regs.c = 0;
}
}

31
bsnes/processor/hg51b/hg51b.hpp Executable file
View File

@ -0,0 +1,31 @@
#ifndef PROCESSOR_HG51B_HPP
#define PROCESSOR_HG51B_HPP
namespace Processor {
//Hitachi HG51B169 (HG51BS family/derivative?)
struct HG51B {
//uint16 programROM[2][256];
uint24 dataROM[1024];
uint8 dataRAM[3072];
#include "registers.hpp"
void exec(uint24 addr);
virtual uint8 bus_read(uint24 addr) = 0;
virtual void bus_write(uint24 addr, uint8 data) = 0;
void power();
void serialize(serializer&);
protected:
void push();
void pull();
unsigned sa();
unsigned ri();
unsigned np();
void instruction();
};
}
#endif

View File

@ -1,6 +1,6 @@
#ifdef HITACHIDSP_CPP
#ifdef PROCESSOR_HG51B_HPP
void HitachiDSP::push() {
void HG51B::push() {
stack[7] = stack[6];
stack[6] = stack[5];
stack[5] = stack[4];
@ -11,7 +11,7 @@ void HitachiDSP::push() {
stack[0] = regs.pc;
}
void HitachiDSP::pull() {
void HG51B::pull() {
regs.pc = stack[0];
stack[0] = stack[1];
stack[1] = stack[2];
@ -24,7 +24,7 @@ void HitachiDSP::pull() {
}
//Shift-A: math opcodes can shift A register prior to ALU operation
unsigned HitachiDSP::sa() {
unsigned HG51B::sa() {
switch(opcode & 0x0300) { default:
case 0x0000: return regs.a << 0;
case 0x0100: return regs.a << 1;
@ -34,18 +34,18 @@ unsigned HitachiDSP::sa() {
}
//Register-or-Immediate: most opcodes can load from a register or immediate
unsigned HitachiDSP::ri() {
unsigned HG51B::ri() {
if(opcode & 0x0400) return opcode & 0xff;
return reg_read(opcode & 0xff);
}
//New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes)
unsigned HitachiDSP::np() {
unsigned HG51B::np() {
if(opcode & 0x0200) return (regs.p << 8) | (opcode & 0xff);
return (regs.pc & 0xffff00) | (opcode & 0xff);
}
void HitachiDSP::exec() {
void HG51B::instruction() {
if((opcode & 0xffff) == 0x0000) {
//0000 0000 0000 0000
//nop
@ -344,12 +344,12 @@ void HitachiDSP::exec() {
else if((opcode & 0xffff) == 0xfc00) {
//1111 1100 0000 0000
//halt
state = State::Idle;
regs.halt = true;
}
else {
print("Hitachi DSP: invalid opcode @ ", hex<4>(regs.pc - 1), " = ", hex<4>(opcode), "\n");
state = State::Idle;
print("Hitachi DSP: unknown opcode @ ", hex<4>(regs.pc - 1), " = ", hex<4>(opcode), "\n");
regs.halt = true;
}
}

View File

@ -1,7 +1,7 @@
#ifdef HITACHIDSP_CPP
#ifdef PROCESSOR_HG51B_HPP
unsigned HitachiDSP::reg_read(unsigned n) const {
switch(n) {
uint24 HG51B::reg_read(uint8 addr) const {
switch(addr) {
case 0x00: return regs.a;
case 0x01: return regs.acch;
case 0x02: return regs.accl;
@ -46,8 +46,8 @@ unsigned HitachiDSP::reg_read(unsigned n) const {
return 0x000000;
}
void HitachiDSP::reg_write(unsigned n, unsigned data) {
switch(n) {
void HG51B::reg_write(uint8 addr, uint24 data) {
switch(addr) {
case 0x00: regs.a = data; return;
case 0x01: regs.acch = data; return;
case 0x02: regs.accl = data; return;

View File

@ -0,0 +1,25 @@
struct Registers {
bool halt;
uint24 pc;
uint16 p;
bool n;
bool z;
bool c;
uint24 a;
uint24 acch;
uint24 accl;
uint24 busdata;
uint24 romdata;
uint24 ramdata;
uint24 busaddr;
uint24 ramaddr;
uint24 gpr[16];
} regs;
uint24 stack[8];
uint16 opcode;
uint24 reg_read(uint8 addr) const;
void reg_write(uint8 addr, uint24 data);

View File

@ -0,0 +1,27 @@
#ifdef PROCESSOR_HG51B_HPP
void HG51B::serialize(serializer &s) {
s.array(dataRAM);
for(auto &n : stack) s.integer(n);
s.integer(opcode);
s.integer(regs.halt);
s.integer(regs.pc);
s.integer(regs.p);
s.integer(regs.n);
s.integer(regs.z);
s.integer(regs.c);
s.integer(regs.a);
s.integer(regs.acch);
s.integer(regs.accl);
s.integer(regs.busdata);
s.integer(regs.romdata);
s.integer(regs.ramdata);
s.integer(regs.busaddr);
s.integer(regs.ramaddr);
for(auto &n : regs.gpr) s.integer(n);
}
#endif

View File

@ -4,8 +4,6 @@
namespace SNES {
#include "memory.cpp"
#include "opcodes.cpp"
#include "registers.cpp"
#include "serialization.cpp"
HitachiDSP hitachidsp;
@ -17,27 +15,17 @@ void HitachiDSP::enter() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
switch(state) {
case State::Idle:
step(1);
break;
case State::DMA:
for(unsigned n = 0; n < regs.dma_length; n++) {
bus.write(regs.dma_target + n, bus.read(regs.dma_source + n));
if(mmio.dma) {
for(unsigned n = 0; n < mmio.dma_length; n++) {
bus.write(mmio.dma_target + n, bus.read(mmio.dma_source + n));
step(2);
}
state = State::Idle;
break;
case State::Execute:
unsigned offset = regs.program_offset + regs.pc * 2;
opcode = bus_read(offset + 0) << 0;
opcode |= bus_read(offset + 1) << 8;
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff);
exec();
step(1);
break;
mmio.dma = false;
}
exec(mmio.program_offset);
step(1);
synchronize_cpu();
}
}
@ -52,27 +40,24 @@ void HitachiDSP::unload() {
}
void HitachiDSP::power() {
mmio.dma = false;
mmio.dma_source = 0x000000;
mmio.dma_length = 0x0000;
mmio.dma_target = 0x000000;
mmio.r1f48 = 0x00;
mmio.program_offset = 0x000000;
mmio.r1f4c = 0x00;
mmio.page_number = 0x0000;
mmio.program_counter = 0x00;
mmio.r1f50 = 0x33;
mmio.r1f51 = 0x00;
mmio.r1f52 = 0x01;
}
void HitachiDSP::reset() {
create(HitachiDSP::Enter, frequency);
state = State::Idle;
regs.n = 0;
regs.z = 0;
regs.c = 0;
regs.dma_source = 0x000000;
regs.dma_length = 0x0000;
regs.dma_target = 0x000000;
regs.r1f48 = 0x00;
regs.program_offset = 0x000000;
regs.r1f4c = 0x00;
regs.page_number = 0x0000;
regs.program_counter = 0x00;
regs.r1f50 = 0x33;
regs.r1f51 = 0x00;
regs.r1f52 = 0x01;
HG51B::power();
}
}

View File

@ -1,15 +1,6 @@
//Hitachi HG51B169
class HitachiDSP : public Coprocessor {
public:
struct HitachiDSP : Processor::HG51B, Coprocessor {
unsigned frequency;
//uint16 programROM[2][256];
uint24 dataROM[1024];
uint8 dataRAM[3072];
uint24 stack[8];
uint16 opcode;
enum class State : unsigned { Idle, DMA, Execute } state;
#include "registers.hpp"
#include "mmio.hpp"
static void Enter();
void enter();
@ -20,28 +11,18 @@ public:
void power();
void reset();
//memory.cpp
uint8 bus_read(unsigned addr);
void bus_write(unsigned addr, uint8 data);
//HG51B read/write
uint8 bus_read(uint24 addr);
void bus_write(uint24 addr, uint8 data);
//CPU ROM read/write
uint8 rom_read(unsigned addr);
void rom_write(unsigned addr, uint8 data);
//CPU MMIO read/write
uint8 dsp_read(unsigned addr);
void dsp_write(unsigned addr, uint8 data);
//opcodes.cpp
void push();
void pull();
unsigned sa();
unsigned ri();
unsigned np();
void exec();
//registers.cpp
unsigned reg_read(unsigned n) const;
void reg_write(unsigned n, unsigned data);
void serialize(serializer&);
};

View File

@ -1,18 +1,18 @@
#ifdef HITACHIDSP_CPP
uint8 HitachiDSP::bus_read(unsigned addr) {
uint8 HitachiDSP::bus_read(uint24 addr) {
if((addr & 0x408000) == 0x008000) return bus.read(addr);
return 0x00;
}
void HitachiDSP::bus_write(unsigned addr, uint8 data) {
void HitachiDSP::bus_write(uint24 addr, uint8 data) {
if((addr & 0x40e000) == 0x006000) return bus.write(addr, data);
}
uint8 HitachiDSP::rom_read(unsigned addr) {
if(co_active() == cpu.thread) {
if(state == State::Idle) return cartridge.rom.read(addr);
if((addr & 0x40ffe0) == 0x00ffe0) return regs.vector[addr & 0x1f];
if(regs.halt) return cartridge.rom.read(addr);
if((addr & 0x40ffe0) == 0x00ffe0) return mmio.vector[addr & 0x1f];
return cpu.regs.mdr;
}
if(co_active() == hitachidsp.thread) {
@ -34,34 +34,34 @@ uint8 HitachiDSP::dsp_read(unsigned addr) {
//MMIO
switch(addr) {
case 0x1f40: return regs.dma_source >> 0;
case 0x1f41: return regs.dma_source >> 8;
case 0x1f42: return regs.dma_source >> 16;
case 0x1f43: return regs.dma_length >> 0;
case 0x1f44: return regs.dma_length >> 8;
case 0x1f45: return regs.dma_target >> 0;
case 0x1f46: return regs.dma_target >> 8;
case 0x1f47: return regs.dma_target >> 16;
case 0x1f48: return regs.r1f48;
case 0x1f49: return regs.program_offset >> 0;
case 0x1f4a: return regs.program_offset >> 8;
case 0x1f4b: return regs.program_offset >> 16;
case 0x1f4c: return regs.r1f4c;
case 0x1f4d: return regs.page_number >> 0;
case 0x1f4e: return regs.page_number >> 8;
case 0x1f4f: return regs.program_counter;
case 0x1f50: return regs.r1f50;
case 0x1f51: return regs.r1f51;
case 0x1f52: return regs.r1f52;
case 0x1f40: return mmio.dma_source >> 0;
case 0x1f41: return mmio.dma_source >> 8;
case 0x1f42: return mmio.dma_source >> 16;
case 0x1f43: return mmio.dma_length >> 0;
case 0x1f44: return mmio.dma_length >> 8;
case 0x1f45: return mmio.dma_target >> 0;
case 0x1f46: return mmio.dma_target >> 8;
case 0x1f47: return mmio.dma_target >> 16;
case 0x1f48: return mmio.r1f48;
case 0x1f49: return mmio.program_offset >> 0;
case 0x1f4a: return mmio.program_offset >> 8;
case 0x1f4b: return mmio.program_offset >> 16;
case 0x1f4c: return mmio.r1f4c;
case 0x1f4d: return mmio.page_number >> 0;
case 0x1f4e: return mmio.page_number >> 8;
case 0x1f4f: return mmio.program_counter;
case 0x1f50: return mmio.r1f50;
case 0x1f51: return mmio.r1f51;
case 0x1f52: return mmio.r1f52;
case 0x1f53: case 0x1f54: case 0x1f55: case 0x1f56:
case 0x1f57: case 0x1f58: case 0x1f59: case 0x1f5a:
case 0x1f5b: case 0x1f5c: case 0x1f5d: case 0x1f5e:
case 0x1f5f: return ((state != State::Idle) << 6) | ((state == State::Idle) << 1);
case 0x1f5f: return ((regs.halt == false) << 6) | ((regs.halt == true) << 1);
}
//Vector
if(addr >= 0x1f60 && addr <= 0x1f7f) {
return regs.vector[addr & 0x1f];
return mmio.vector[addr & 0x1f];
}
//GPRs
@ -85,37 +85,37 @@ void HitachiDSP::dsp_write(unsigned addr, uint8 data) {
//MMIO
switch(addr) {
case 0x1f40: regs.dma_source = (regs.dma_source & 0xffff00) | (data << 0); return;
case 0x1f41: regs.dma_source = (regs.dma_source & 0xff00ff) | (data << 8); return;
case 0x1f42: regs.dma_source = (regs.dma_source & 0x00ffff) | (data << 16); return;
case 0x1f43: regs.dma_length = (regs.dma_length & 0xff00) | (data << 0); return;
case 0x1f44: regs.dma_length = (regs.dma_length & 0x00ff) | (data << 8); return;
case 0x1f45: regs.dma_target = (regs.dma_target & 0xffff00) | (data << 0); return;
case 0x1f46: regs.dma_target = (regs.dma_target & 0xff00ff) | (data << 8); return;
case 0x1f47: regs.dma_target = (regs.dma_target & 0x00ffff) | (data << 16);
if(state == State::Idle) state = State::DMA;
case 0x1f40: mmio.dma_source = (mmio.dma_source & 0xffff00) | (data << 0); return;
case 0x1f41: mmio.dma_source = (mmio.dma_source & 0xff00ff) | (data << 8); return;
case 0x1f42: mmio.dma_source = (mmio.dma_source & 0x00ffff) | (data << 16); return;
case 0x1f43: mmio.dma_length = (mmio.dma_length & 0xff00) | (data << 0); return;
case 0x1f44: mmio.dma_length = (mmio.dma_length & 0x00ff) | (data << 8); return;
case 0x1f45: mmio.dma_target = (mmio.dma_target & 0xffff00) | (data << 0); return;
case 0x1f46: mmio.dma_target = (mmio.dma_target & 0xff00ff) | (data << 8); return;
case 0x1f47: mmio.dma_target = (mmio.dma_target & 0x00ffff) | (data << 16);
if(regs.halt) mmio.dma = true;
return;
case 0x1f48: regs.r1f48 = data & 0x01; return;
case 0x1f49: regs.program_offset = (regs.program_offset & 0xffff00) | (data << 0); return;
case 0x1f4a: regs.program_offset = (regs.program_offset & 0xff00ff) | (data << 8); return;
case 0x1f4b: regs.program_offset = (regs.program_offset & 0x00ffff) | (data << 16); return;
case 0x1f4c: regs.r1f4c = data & 0x03; return;
case 0x1f4d: regs.page_number = (regs.page_number & 0x7f00) | ((data & 0xff) << 0); return;
case 0x1f4e: regs.page_number = (regs.page_number & 0x00ff) | ((data & 0x7f) << 8); return;
case 0x1f4f: regs.program_counter = data;
if(state == State::Idle) {
regs.pc = regs.page_number * 256 + regs.program_counter;
state = State::Execute;
case 0x1f48: mmio.r1f48 = data & 0x01; return;
case 0x1f49: mmio.program_offset = (mmio.program_offset & 0xffff00) | (data << 0); return;
case 0x1f4a: mmio.program_offset = (mmio.program_offset & 0xff00ff) | (data << 8); return;
case 0x1f4b: mmio.program_offset = (mmio.program_offset & 0x00ffff) | (data << 16); return;
case 0x1f4c: mmio.r1f4c = data & 0x03; return;
case 0x1f4d: mmio.page_number = (mmio.page_number & 0x7f00) | ((data & 0xff) << 0); return;
case 0x1f4e: mmio.page_number = (mmio.page_number & 0x00ff) | ((data & 0x7f) << 8); return;
case 0x1f4f: mmio.program_counter = data;
if(regs.halt) {
regs.pc = mmio.page_number * 256 + mmio.program_counter;
regs.halt = false;
}
return;
case 0x1f50: regs.r1f50 = data & 0x77; return;
case 0x1f51: regs.r1f51 = data & 0x01; return;
case 0x1f52: regs.r1f52 = data & 0x01; return;
case 0x1f50: mmio.r1f50 = data & 0x77; return;
case 0x1f51: mmio.r1f51 = data & 0x01; return;
case 0x1f52: mmio.r1f52 = data & 0x01; return;
}
//Vector
if(addr >= 0x1f60 && addr <= 0x1f7f) {
regs.vector[addr & 0x1f] = data;
mmio.vector[addr & 0x1f] = data;
return;
}

View File

@ -1,21 +1,6 @@
struct Registers {
uint24 pc;
uint16 p;
bool n;
bool z;
bool c;
struct MMIO {
bool dma; //true during DMA transfers
uint24 a;
uint24 acch;
uint24 accl;
uint24 busdata;
uint24 romdata;
uint24 ramdata;
uint24 busaddr;
uint24 ramaddr;
uint24 gpr[16];
//MMIO
uint24 dma_source; //$1f40-$1f42
uint24 dma_length; //$1f43-$1f44
uint24 dma_target; //$1f45-$1f47
@ -28,4 +13,4 @@ struct Registers {
uint8 r1f51; //$1f51
uint8 r1f52; //$1f52
uint8 vector[32]; //$1f60-$1f7f
} regs;
} mmio;

View File

@ -1,41 +1,22 @@
#ifdef HITACHIDSP_CPP
void HitachiDSP::serialize(serializer &s) {
HG51B::serialize(s);
Thread::serialize(s);
s.array(dataRAM);
for(auto &n : stack) s.integer(n);
s.integer(opcode);
s.integer((unsigned&)state);
s.integer(regs.pc);
s.integer(regs.p);
s.integer(regs.n);
s.integer(regs.z);
s.integer(regs.c);
s.integer(regs.a);
s.integer(regs.acch);
s.integer(regs.accl);
s.integer(regs.busdata);
s.integer(regs.romdata);
s.integer(regs.ramdata);
s.integer(regs.busaddr);
s.integer(regs.ramaddr);
for(auto &n : regs.gpr) s.integer(n);
s.integer(regs.dma_source);
s.integer(regs.dma_length);
s.integer(regs.dma_target);
s.integer(regs.r1f48);
s.integer(regs.program_offset);
s.integer(regs.r1f4c);
s.integer(regs.page_number);
s.integer(regs.program_counter);
s.integer(regs.r1f50);
s.integer(regs.r1f51);
s.integer(regs.r1f52);
s.array(regs.vector);
s.integer(mmio.dma);
s.integer(mmio.dma_source);
s.integer(mmio.dma_length);
s.integer(mmio.dma_target);
s.integer(mmio.r1f48);
s.integer(mmio.program_offset);
s.integer(mmio.r1f4c);
s.integer(mmio.page_number);
s.integer(mmio.program_counter);
s.integer(mmio.r1f50);
s.integer(mmio.r1f51);
s.integer(mmio.r1f52);
s.array(mmio.vector);
}
#endif

View File

@ -3,6 +3,7 @@
#include <base/base.hpp>
#include <processor/arm/arm.hpp>
#include <processor/hg51b/hg51b.hpp>
namespace SNES {
namespace Info {

View File

@ -1,6 +1,6 @@
options += debugger
processor := arm
processor := arm hg51b
include processor/Makefile
include $(snes)/Makefile

View File

@ -1,4 +1,4 @@
processor := arm
processor := arm hg51b
include processor/Makefile
include $(snes)/Makefile

View File

@ -1,4 +1,4 @@
processors := arm
processors := arm hg51b
include processor/Makefile
include $(nes)/Makefile