mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-11 01:11:42 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
842ea44b90 | ||
|
|
e67dfac166 | ||
|
|
a5d2ee5e5f | ||
|
|
bef70d2550 | ||
|
|
f656e317c8 | ||
|
|
494b1cb0ba | ||
|
|
be25bb110c | ||
|
|
1f59ef7457 | ||
|
|
74e6d389a6 | ||
|
|
9449b78675 | ||
|
|
388f555304 | ||
|
|
4cf677063c | ||
|
|
890b56f84d | ||
|
|
ddd3a439ff | ||
|
|
318beb509f | ||
|
|
6f6e2ea260 | ||
|
|
67cf63b532 | ||
|
|
a427fe2a71 | ||
|
|
e6c42199aa | ||
|
|
b224caf4bd | ||
|
|
bd565eb982 | ||
|
|
f149b5d2a5 | ||
|
|
32d8abd372 | ||
|
|
5ef188c876 | ||
|
|
590773508e | ||
|
|
2f24221be9 | ||
|
|
e7b120f2e5 | ||
|
|
4a1084179b | ||
|
|
e9bd3dda68 | ||
|
|
c1f9897a03 | ||
|
|
291a0ea8de |
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "112";
|
||||
static const string Version = "113";
|
||||
static const string License = "GPLv3+";
|
||||
static const string Website = "https://ares.dev";
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ struct Port : Object {
|
||||
auto type() const -> string { return _type; }
|
||||
auto family() const -> string { return _family; }
|
||||
auto hotSwappable() const -> bool { return _hotSwappable; }
|
||||
auto supported() const -> vector<string> { return _supported; }
|
||||
|
||||
auto setAllocate(function<Node::Peripheral (string)> allocate) -> void { _allocate = allocate; }
|
||||
auto setConnect(function<void ()> connect) -> void { _connect = connect; }
|
||||
@@ -12,6 +13,7 @@ struct Port : Object {
|
||||
auto setType(string type) -> void { _type = type; }
|
||||
auto setFamily(string family) -> void { _family = family; }
|
||||
auto setHotSwappable(bool hotSwappable) -> void { _hotSwappable = hotSwappable; }
|
||||
auto setSupported(vector<string> supported) -> void { _supported = supported; }
|
||||
|
||||
auto connected() -> Node::Peripheral {
|
||||
return find<Node::Peripheral>(0);
|
||||
@@ -68,4 +70,5 @@ protected:
|
||||
string _type;
|
||||
string _family;
|
||||
bool _hotSwappable = false;
|
||||
vector<string> _supported;
|
||||
};
|
||||
@@ -1,3 +1,18 @@
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using i128 = int128_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using u128 = uint128_t;
|
||||
|
||||
using f32 = float32_t;
|
||||
using f64 = float64_t;
|
||||
|
||||
using int1 = nall::Integer< 1>;
|
||||
using int2 = nall::Integer< 2>;
|
||||
using int3 = nall::Integer< 3>;
|
||||
|
||||
@@ -21,7 +21,6 @@ ares.objects += $(if $(findstring spc700,$(ares.components)),ares-processor-spc7
|
||||
ares.objects += $(if $(findstring tlcs900h,$(ares.components)),ares-processor-tlcs900h)
|
||||
ares.objects += $(if $(findstring upd96050,$(ares.components)),ares-processor-upd96050)
|
||||
ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz)
|
||||
ares.objects += $(if $(findstring vr4300,$(ares.components)),ares-processor-vr4300)
|
||||
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
|
||||
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
|
||||
|
||||
@@ -49,7 +48,6 @@ $(object.path)/ares-processor-spc700.o: $(ares.path)/component/processor/spc70
|
||||
$(object.path)/ares-processor-tlcs900h.o: $(ares.path)/component/processor/tlcs900h/tlcs900h.cpp
|
||||
$(object.path)/ares-processor-upd96050.o: $(ares.path)/component/processor/upd96050/upd96050.cpp
|
||||
$(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz/v30mz.cpp
|
||||
$(object.path)/ares-processor-vr4300.o: $(ares.path)/component/processor/vr4300/vr4300.cpp
|
||||
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
|
||||
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
auto VR4300::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "vr4300.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto VR4300::power() -> void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//NEC VR4300
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct VR4300 {
|
||||
//vr4300.cpp
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -11,6 +11,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Control Pad", "Fighting Pad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -13,6 +13,7 @@ auto Keyboard::load(Node::Object parent) -> void {
|
||||
port->setAllocate([&](auto name) { return allocate(port, name); });
|
||||
port->setConnect([&] { connect(); });
|
||||
port->setDisconnect([&] { disconnect(); });
|
||||
port->setSupported({"Japanese"});
|
||||
}
|
||||
|
||||
auto Keyboard::unload() -> void {
|
||||
|
||||
+17
-4
@@ -1,13 +1,26 @@
|
||||
ares.components += vr4300
|
||||
|
||||
ares.objects += ares-n64-interface ares-n64-system
|
||||
ares.objects += ares-n64-interface ares-n64-memory ares-n64-system
|
||||
ares.objects += ares-n64-cartridge ares-n64-controller
|
||||
ares.objects += ares-n64-cpu ares-n64-rdp ares-n64-rsp
|
||||
ares.objects += ares-n64-mi ares-n64-vi ares-n64-ai
|
||||
ares.objects += ares-n64-pi ares-n64-ri ares-n64-si
|
||||
ares.objects += ares-n64-rdram ares-n64-cpu ares-n64-rdp ares-n64-rsp
|
||||
|
||||
$(object.path)/ares-n64-interface.o: $(ares.path)/n64/interface/interface.cpp
|
||||
$(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp
|
||||
$(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp
|
||||
$(object.path)/ares-n64-cartridge.o: $(ares.path)/n64/cartridge/cartridge.cpp
|
||||
$(object.path)/ares-n64-controller.o: $(ares.path)/n64/controller/controller.cpp
|
||||
$(object.path)/ares-n64-sp.o: $(ares.path)/n64/sp/sp.cpp
|
||||
$(object.path)/ares-n64-dp.o: $(ares.path)/n64/dp/dp.cpp
|
||||
$(object.path)/ares-n64-mi.o: $(ares.path)/n64/mi/mi.cpp
|
||||
$(object.path)/ares-n64-vi.o: $(ares.path)/n64/vi/vi.cpp
|
||||
$(object.path)/ares-n64-ai.o: $(ares.path)/n64/ai/ai.cpp
|
||||
$(object.path)/ares-n64-pi.o: $(ares.path)/n64/pi/pi.cpp
|
||||
$(object.path)/ares-n64-ri.o: $(ares.path)/n64/ri/ri.cpp
|
||||
$(object.path)/ares-n64-si.o: $(ares.path)/n64/si/si.cpp
|
||||
$(object.path)/ares-n64-rdram.o: $(ares.path)/n64/rdram/rdram.cpp
|
||||
$(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp
|
||||
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp
|
||||
$(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp
|
||||
|
||||
flags += -msse4.2
|
||||
options += -msse4.2
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
AI ai;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto AI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("AI");
|
||||
|
||||
stream = node->append<Node::Stream>("AI");
|
||||
stream->setChannels(2);
|
||||
stream->setFrequency(44100.0);
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto AI::unload() -> void {
|
||||
node = {};
|
||||
stream = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto AI::main() -> void {
|
||||
sample();
|
||||
step(dac.period);
|
||||
}
|
||||
|
||||
auto AI::sample() -> void {
|
||||
if(io.dmaCount == 0) return stream->sample(0.0, 0.0);
|
||||
|
||||
auto data = rdram.ram.readWord(io.dmaAddress[0]);
|
||||
auto left = i16(data >> 16);
|
||||
auto right = i16(data >> 0);
|
||||
stream->sample(left / 32768.0, right / 32768.0);
|
||||
|
||||
io.dmaAddress[0] += 4;
|
||||
io.dmaLength [0] -= 4;
|
||||
if(!io.dmaLength[0]) {
|
||||
mi.raise(MI::IRQ::AI);
|
||||
if(--io.dmaCount) {
|
||||
io.dmaAddress[0] = io.dmaAddress[1];
|
||||
io.dmaLength [0] = io.dmaLength [1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto AI::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
auto AI::power() -> void {
|
||||
Thread::reset();
|
||||
io = {};
|
||||
dac = {};
|
||||
stream->setFrequency(dac.frequency);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//Audio Interface
|
||||
|
||||
struct AI : Thread {
|
||||
Node::Component node;
|
||||
Node::Stream stream;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//ai.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto main() -> void;
|
||||
auto sample() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct FIFO {
|
||||
uint24 address = 0;
|
||||
} fifo[2];
|
||||
|
||||
struct IO {
|
||||
uint1 dmaEnable = 0;
|
||||
uint24 dmaAddress[2] = {};
|
||||
uint18 dmaLength[2] = {};
|
||||
uint2 dmaCount = 0;
|
||||
uint14 dacRate = 0;
|
||||
uint4 bitRate = 0;
|
||||
} io;
|
||||
|
||||
struct DAC {
|
||||
u32 frequency = 44100;
|
||||
u32 precision = 16;
|
||||
u32 period = 93'750'000 / 44'100;
|
||||
} dac;
|
||||
};
|
||||
|
||||
extern AI ai;
|
||||
@@ -0,0 +1,9 @@
|
||||
auto AI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "AI");
|
||||
}
|
||||
|
||||
auto AI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
static const vector<string> registerNames = {
|
||||
"AI_DRAM_ADDRESS",
|
||||
"AI_LENGTH",
|
||||
"AI_CONTROL",
|
||||
"AI_STATUS",
|
||||
"AI_DACRATE",
|
||||
"AI_BITRATE",
|
||||
};
|
||||
|
||||
auto AI::readIO(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address != 3) {
|
||||
//AI_LENGTH (mirrored)
|
||||
data.bit(0,17) = io.dmaLength[0];
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//AI_STATUS
|
||||
data.bit( 0) = io.dmaCount > 1;
|
||||
data.bit(20) = 1;
|
||||
data.bit(24) = 1;
|
||||
data.bit(30) = io.dmaCount > 0;
|
||||
data.bit(31) = io.dmaCount > 1;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "AI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto AI::writeIO(u32 address, uint32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//AI_DRAM_ADDRESS
|
||||
if(io.dmaCount < 2) {
|
||||
io.dmaAddress[io.dmaCount] = data.bit(0,23) & ~7;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//AI_LENGTH
|
||||
uint18 length = data.bit(0,17) & ~7;
|
||||
if(io.dmaCount < 2 && length) {
|
||||
io.dmaLength[io.dmaCount] = length;
|
||||
io.dmaCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//AI_CONTROL
|
||||
io.dmaEnable = data.bit(0);
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//AI_STATUS
|
||||
mi.lower(MI::IRQ::AI);
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//AI_DACRATE
|
||||
auto frequency = dac.frequency;
|
||||
io.dacRate = data.bit(0,13);
|
||||
dac.frequency = max(1, 93'750'000 / 2 / (io.dacRate + 1)) * 1.037;
|
||||
dac.period = 93'750'000 / dac.frequency;
|
||||
if(frequency != dac.frequency) stream->setFrequency(dac.frequency);
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//AI_BITRATE
|
||||
io.bitRate = data.bit(0,3);
|
||||
dac.precision = io.bitRate + 1;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "AI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
auto AI::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
}
|
||||
@@ -21,6 +21,8 @@ auto Cartridge::connect() -> void {
|
||||
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
information.name = document["game/label"].string();
|
||||
information.region = document["game/region"].string();
|
||||
information.cic = document["game/board/cic"].string();
|
||||
|
||||
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
|
||||
rom.allocate(memory["size"].natural());
|
||||
@@ -35,6 +37,7 @@ auto Cartridge::connect() -> void {
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
rom.reset();
|
||||
node = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
Memory::Readable<uint8> rom;
|
||||
Memory rom;
|
||||
|
||||
auto manifest() const -> string { return information.manifest; }
|
||||
auto name() const -> string { return information.name; }
|
||||
auto region() const -> string { return information.region; }
|
||||
auto cic() const -> string { return information.cic; }
|
||||
|
||||
//cartridge.cpp
|
||||
auto allocate(Node::Port) -> Node::Peripheral;
|
||||
@@ -20,6 +22,8 @@ private:
|
||||
struct Information {
|
||||
string manifest;
|
||||
string name;
|
||||
string region;
|
||||
string cic;
|
||||
} information;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ struct Controller {
|
||||
Node::Peripheral node;
|
||||
|
||||
virtual ~Controller() = default;
|
||||
virtual auto read() -> uint32 { return 0; }
|
||||
};
|
||||
|
||||
#include "port.hpp"
|
||||
|
||||
@@ -1,3 +1,60 @@
|
||||
Gamepad::Gamepad(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("Gamepad");
|
||||
|
||||
x = node->append<Node::Axis> ("X-axis");
|
||||
y = node->append<Node::Axis> ("Y-axis");
|
||||
up = node->append<Node::Button>("Up");
|
||||
down = node->append<Node::Button>("Down");
|
||||
left = node->append<Node::Button>("Left");
|
||||
right = node->append<Node::Button>("Right");
|
||||
b = node->append<Node::Button>("B");
|
||||
a = node->append<Node::Button>("A");
|
||||
cameraUp = node->append<Node::Button>("C-Up");
|
||||
cameraDown = node->append<Node::Button>("C-Down");
|
||||
cameraLeft = node->append<Node::Button>("C-Left");
|
||||
cameraRight = node->append<Node::Button>("C-Right");
|
||||
l = node->append<Node::Button>("L");
|
||||
r = node->append<Node::Button>("R");
|
||||
z = node->append<Node::Button>("Z");
|
||||
start = node->append<Node::Button>("Start");
|
||||
}
|
||||
|
||||
auto Gamepad::read() -> uint32 {
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(b);
|
||||
platform->input(a);
|
||||
platform->input(cameraUp);
|
||||
platform->input(cameraDown);
|
||||
platform->input(cameraLeft);
|
||||
platform->input(cameraRight);
|
||||
platform->input(l);
|
||||
platform->input(r);
|
||||
platform->input(z);
|
||||
platform->input(start);
|
||||
|
||||
uint32 data;
|
||||
data.byte(0) = y->value() >> 8;
|
||||
data.byte(1) = x->value() >> 8;
|
||||
data.bit(16) = cameraRight->value();
|
||||
data.bit(17) = cameraLeft->value();
|
||||
data.bit(18) = cameraDown->value();
|
||||
data.bit(19) = cameraUp->value();
|
||||
data.bit(20) = l->value();
|
||||
data.bit(21) = r->value();
|
||||
data.bit(22) = 0;
|
||||
data.bit(23) = 0;
|
||||
data.bit(24) = right->value() & !left->value();
|
||||
data.bit(25) = left->value() & !right->value();
|
||||
data.bit(26) = down->value() & !up->value();
|
||||
data.bit(27) = up->value() & !down->value();
|
||||
data.bit(28) = start->value();
|
||||
data.bit(29) = z->value();
|
||||
data.bit(30) = b->value();
|
||||
data.bit(31) = a->value();
|
||||
return data;
|
||||
}
|
||||
@@ -1,3 +1,21 @@
|
||||
struct Gamepad : Controller {
|
||||
Node::Axis x;
|
||||
Node::Axis y;
|
||||
Node::Button up;
|
||||
Node::Button down;
|
||||
Node::Button left;
|
||||
Node::Button right;
|
||||
Node::Button b;
|
||||
Node::Button a;
|
||||
Node::Button cameraUp;
|
||||
Node::Button cameraDown;
|
||||
Node::Button cameraLeft;
|
||||
Node::Button cameraRight;
|
||||
Node::Button l;
|
||||
Node::Button r;
|
||||
Node::Button z;
|
||||
Node::Button start;
|
||||
|
||||
Gamepad(Node::Port);
|
||||
auto read() -> uint32 override;
|
||||
};
|
||||
@@ -12,6 +12,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "registers.hpp"
|
||||
#include "scc-tlb.cpp"
|
||||
#include "scc-registers.cpp"
|
||||
#include "fpu-registers.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "cpu-instructions.cpp"
|
||||
#include "scc-instructions.cpp"
|
||||
#include "fpu-instructions.cpp"
|
||||
#include "disassembler.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::powerR4300(bool reset) -> void {
|
||||
for(uint n : range(32)) GPR[n].u64 = 0;
|
||||
scc = {};
|
||||
scc.status.softReset = 1; //reset;
|
||||
fpu = {};
|
||||
LO.u64 = 0;
|
||||
HI.u64 = 0;
|
||||
GPR[Core::Register::SP].u64 = i32(0xa400'1ff0);
|
||||
PC = i32(0xbfc00000);
|
||||
branch.reset();
|
||||
fesetround(FE_TONEAREST);
|
||||
context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::Context::setMode() -> void {
|
||||
mode = min(2, self.scc.status.privilegeMode);
|
||||
if(self.scc.status.exceptionLevel) mode = Mode::Kernel;
|
||||
if(self.scc.status.errorLevel) mode = Mode::Kernel;
|
||||
|
||||
segment[0] = Segment::Mapped;
|
||||
segment[1] = Segment::Mapped;
|
||||
segment[2] = Segment::Mapped;
|
||||
segment[3] = Segment::Mapped;
|
||||
if(mode == Mode::Kernel) {
|
||||
segment[4] = Segment::Cached;
|
||||
segment[5] = Segment::Uncached;
|
||||
segment[6] = Segment::Mapped;
|
||||
segment[7] = Segment::Mapped;
|
||||
}
|
||||
if(mode == Mode::Supervisor) {
|
||||
segment[4] = Segment::Invalid;
|
||||
segment[5] = Segment::Invalid;
|
||||
segment[6] = Segment::Mapped;
|
||||
segment[7] = Segment::Invalid;
|
||||
}
|
||||
if(mode == Mode::User) {
|
||||
segment[4] = Segment::Invalid;
|
||||
segment[5] = Segment::Invalid;
|
||||
segment[6] = Segment::Invalid;
|
||||
segment[7] = Segment::Invalid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//{
|
||||
//core.cpp
|
||||
auto powerR4300(bool reset) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readAddress (u32 address) -> maybe<u32>;
|
||||
auto writeAddress(u32 address) -> maybe<u32>;
|
||||
|
||||
auto readByte (u32 address) -> maybe<u32>;
|
||||
auto readHalf (u32 address) -> maybe<u32>;
|
||||
auto readWord (u32 address) -> maybe<u32>;
|
||||
auto readDouble (u32 address) -> maybe<u64>;
|
||||
|
||||
auto writeByte (u32 address, u8 data) -> bool;
|
||||
auto writeHalf (u32 address, u16 data) -> bool;
|
||||
auto writeWord (u32 address, u32 data) -> bool;
|
||||
auto writeDouble (u32 address, u64 data) -> bool;
|
||||
|
||||
//instruction.cpp
|
||||
auto raiseException(uint, uint = 0) -> void;
|
||||
auto instruction() -> void;
|
||||
auto instructionDEBUG() -> void;
|
||||
auto instructionEXECUTE() -> void;
|
||||
auto instructionSPECIAL() -> void;
|
||||
auto instructionREGIMM() -> void;
|
||||
auto instructionCOP0() -> void;
|
||||
auto instructionCOP1() -> void;
|
||||
|
||||
enum Interrupt : uint {
|
||||
Software0 = 0,
|
||||
Software1 = 1,
|
||||
RCP = 2,
|
||||
Cartridge = 3,
|
||||
Reset = 4,
|
||||
ReadRDB = 5,
|
||||
WriteRDB = 6,
|
||||
Timer = 7,
|
||||
};
|
||||
|
||||
//serialization.cpp
|
||||
auto serializeR4300(serializer&) -> void;
|
||||
|
||||
struct Context {
|
||||
CPU& self;
|
||||
Context(CPU& self) : self(self) {}
|
||||
|
||||
enum Mode : uint { Kernel, Supervisor, User };
|
||||
enum Segment : uint { Invalid, Mapped, Cached, Uncached };
|
||||
uint mode;
|
||||
uint segment[8]; //512_MiB chunks
|
||||
|
||||
//core.cpp
|
||||
auto setMode() -> void;
|
||||
} context{*this};
|
||||
|
||||
#include "exception.hpp"
|
||||
#include "pipeline.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include "scc.hpp"
|
||||
#include "fpu.hpp"
|
||||
#include "disassembler.hpp"
|
||||
|
||||
static constexpr bool Endian = 1; //0 = little, 1 = big
|
||||
static constexpr uint FlipLE = (Endian == 0 ? 7 : 0);
|
||||
static constexpr uint FlipBE = (Endian == 1 ? 7 : 0);
|
||||
//};
|
||||
@@ -0,0 +1,538 @@
|
||||
auto CPU::instructionADD() -> void {
|
||||
//if(RS.u32 > ~RT.u32) return exception.arithmeticOverflow();
|
||||
RD.u64 = i32(RS.u32 + RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDI() -> void {
|
||||
//if(RS.i32 > ~IMMi16) return exception.arithmeticOverflow();
|
||||
RT.u64 = i32(RS.u32 + IMMi16);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDIU() -> void {
|
||||
RT.u64 = i32(RS.u32 + IMMi16);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDU() -> void {
|
||||
RD.u64 = i32(RS.u32 + RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionAND() -> void {
|
||||
RD.u64 = RS.u64 & RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionANDI() -> void {
|
||||
RT.u64 = RS.u64 & IMMu16;
|
||||
}
|
||||
|
||||
auto CPU::instructionB(bool take) -> void {
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBAL(bool take) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBALL(bool take) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBL(bool take) -> void {
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBREAK() -> void {
|
||||
exception.breakpoint();
|
||||
}
|
||||
|
||||
auto CPU::instructionCACHE() -> void {
|
||||
//todo
|
||||
uint cache = OP >> 16 & 3;
|
||||
uint operation = OP >> 18 & 3;
|
||||
if(cache == 0) {
|
||||
//instruction
|
||||
if(operation == 0) {
|
||||
//index invalidate
|
||||
}
|
||||
if(operation == 1) {
|
||||
//index load tag
|
||||
}
|
||||
if(operation == 2) {
|
||||
//index store tag
|
||||
}
|
||||
if(operation == 4) {
|
||||
//hit invalidate
|
||||
}
|
||||
if(operation == 5) {
|
||||
//fill
|
||||
}
|
||||
if(operation == 6) {
|
||||
//hit write back
|
||||
}
|
||||
}
|
||||
if(cache == 1) {
|
||||
//data
|
||||
if(operation == 0) {
|
||||
//index write back invalidate
|
||||
}
|
||||
if(operation == 1) {
|
||||
//index load tag
|
||||
}
|
||||
if(operation == 2) {
|
||||
//index store tag
|
||||
}
|
||||
if(operation == 3) {
|
||||
//create dirty exclusive
|
||||
}
|
||||
if(operation == 4) {
|
||||
//hit invalidate
|
||||
}
|
||||
if(operation == 5) {
|
||||
//hit write back invalidate
|
||||
}
|
||||
if(operation == 6) {
|
||||
//hit write back
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDADD() -> void {
|
||||
//if(RS.u64 > ~RT.u64) return exception.arithmeticOverflow();
|
||||
RD.u64 = RS.u64 + RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDADDI() -> void {
|
||||
//if(RS.i64 > ~IMM16i) return exception.arithmeticOverflow();
|
||||
RT.u64 = RS.u64 + i64(IMMi16);
|
||||
}
|
||||
|
||||
auto CPU::instructionDADDIU() -> void {
|
||||
RT.u64 = RS.u64 + i64(IMMi16);
|
||||
}
|
||||
|
||||
auto CPU::instructionDADDU() -> void {
|
||||
RD.u64 = RS.u64 + RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDDIV() -> void {
|
||||
if(RT.i64) {
|
||||
//cast to i128 to prevent exception on INT64_MIN / -1
|
||||
LO.u64 = i128(RS.i64) / i128(RT.i64);
|
||||
HI.u64 = i128(RS.i64) % i128(RT.i64);
|
||||
} else {
|
||||
LO.u64 = RS.i64 < 0 ? +1 : -1;
|
||||
HI.u64 = RS.i64;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDDIVU() -> void {
|
||||
if(RT.u64) {
|
||||
LO.u64 = RS.u64 / RT.u64;
|
||||
HI.u64 = RS.u64 % RT.u64;
|
||||
} else {
|
||||
LO.u64 = -1;
|
||||
HI.u64 = RS.u64;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDIV() -> void {
|
||||
if(RT.i32) {
|
||||
//cast to i64 to prevent exception on INT32_MIN / -1
|
||||
LO.u64 = i32(i64(RS.i32) / i64(RT.i32));
|
||||
HI.u64 = i32(i64(RS.i32) % i64(RT.i32));
|
||||
} else {
|
||||
LO.u64 = RS.i32 < 0 ? +1 : -1;
|
||||
HI.u64 = RS.i32;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDIVU() -> void {
|
||||
if(RT.u32) {
|
||||
LO.u64 = i32(RS.u32 / RT.u32);
|
||||
HI.u64 = i32(RS.u32 % RT.u32);
|
||||
} else {
|
||||
LO.u64 = -1;
|
||||
HI.u64 = RS.i32;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDMULT() -> void {
|
||||
u128 result = RS.i128() * RT.i128();
|
||||
LO.u64 = result >> 0;
|
||||
HI.u64 = result >> 64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDMULTU() -> void {
|
||||
u128 result = RS.u128() * RT.u128();
|
||||
LO.u64 = result >> 0;
|
||||
HI.u64 = result >> 64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLL() -> void {
|
||||
RD.u64 = RT.u64 << SA;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLL32() -> void {
|
||||
RD.u64 = RT.u64 << SA + 32;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLLV() -> void {
|
||||
RD.u64 = RT.u64 << (RS.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRA() -> void {
|
||||
RD.u64 = RT.i64 >> SA;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRA32() -> void {
|
||||
RD.u64 = RT.i64 >> SA + 32;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRAV() -> void {
|
||||
RD.u64 = RT.i64 >> (RS.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRL() -> void {
|
||||
RD.u64 = RT.u64 >> SA;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRL32() -> void {
|
||||
RD.u64 = RT.u64 >> SA + 32;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRLV() -> void {
|
||||
RD.u64 = RT.u64 >> (RS.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSUB() -> void {
|
||||
//if(RS.u64 < RT.u64) return exception.arithmeticOverflow();
|
||||
RD.u64 = RS.u64 - RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSUBU() -> void {
|
||||
RD.u64 = RS.u64 - RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionJ() -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (IMMu26 << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionJAL() -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (IMMu26 << 2));
|
||||
RA.u64 = i32(PC + 8);
|
||||
}
|
||||
|
||||
auto CPU::instructionJALR() -> void {
|
||||
branch.take(RS.u32);
|
||||
RD.u64 = i32(PC + 8);
|
||||
}
|
||||
|
||||
auto CPU::instructionJR() -> void {
|
||||
branch.take(RS.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionLB() -> void {
|
||||
if(auto data = readByte(RS.u32 + IMMi16)) RT.u64 = i8(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLBU() -> void {
|
||||
if(auto data = readByte(RS.u32 + IMMi16)) RT.u64 = u8(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLD() -> void {
|
||||
if(auto data = readDouble(RS.u32 + IMMi16)) RT.u64 = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionLDL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipLE) & 7);
|
||||
auto mask = u64(0) - 1 << shift;
|
||||
if(auto data = readDouble(address & ~7)) {
|
||||
RT.u64 = RT.u64 & ~mask | *data << shift;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLDR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipBE) & 7);
|
||||
auto mask = u64(0) - 1 >> shift;
|
||||
if(auto data = readDouble(address & ~7)) {
|
||||
RT.u64 = RT.u64 & ~mask | *data >> shift;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLH() -> void {
|
||||
if(auto data = readHalf(RS.u32 + IMMi16)) RT.u64 = i16(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLHU() -> void {
|
||||
if(auto data = readHalf(RS.u32 + IMMi16)) RT.u64 = u16(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLL() -> void {
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) {
|
||||
RT.u64 = i32(*data);
|
||||
scc.ll = tlb.physicalAddress >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLLD() -> void {
|
||||
if(auto data = readDouble(RS.u32 + IMMi16)) {
|
||||
RT.u64 = *data;
|
||||
scc.ll = tlb.physicalAddress >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLUI() -> void {
|
||||
RT.u64 = i32(IMMu16 << 16);
|
||||
}
|
||||
|
||||
auto CPU::instructionLW() -> void {
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) RT.u64 = i32(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLWL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipLE) & 3);
|
||||
auto mask = u32(0) - 1 << shift;
|
||||
if(auto data = readWord(address & ~3)) {
|
||||
RT.u64 = i32(RT.u32 & ~mask | *data << shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLWR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipBE) & 3);
|
||||
auto mask = u32(0) - 1 >> shift;
|
||||
if(auto data = readWord(address & ~3)) {
|
||||
RT.u64 = i32(RT.u32 & ~mask | *data >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLWU() -> void {
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) RT.u64 = u32(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionMFHI() -> void {
|
||||
RD.u64 = HI.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMFLO() -> void {
|
||||
RD.u64 = LO.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMTHI() -> void {
|
||||
HI.u64 = RS.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMTLO() -> void {
|
||||
LO.u64 = RS.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMULT() -> void {
|
||||
u64 result = i64(RS.i32) * i64(RT.i32);
|
||||
LO.u64 = i32(result >> 0);
|
||||
HI.u64 = i32(result >> 32);
|
||||
}
|
||||
|
||||
auto CPU::instructionMULTU() -> void {
|
||||
u64 result = u64(RS.u32) * u64(RT.u32);
|
||||
LO.u64 = i32(result >> 0);
|
||||
HI.u64 = i32(result >> 32);
|
||||
}
|
||||
|
||||
auto CPU::instructionNOR() -> void {
|
||||
RD.u64 = ~(RS.u64 | RT.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionOR() -> void {
|
||||
RD.u64 = RS.u64 | RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionORI() -> void {
|
||||
RT.u64 = RS.u64 | IMMu16;
|
||||
}
|
||||
|
||||
auto CPU::instructionSB() -> void {
|
||||
writeByte(RS.u32 + IMMi16, RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSC() -> void {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
RT.u64 = writeWord(RS.u32 + IMMi16, RT.u32);
|
||||
} else {
|
||||
RT.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSCD() -> void {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
RT.u64 = writeDouble(RS.u32 + IMMi16, RT.u64);
|
||||
} else {
|
||||
RT.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSD() -> void {
|
||||
writeDouble(RS.u32 + IMMi16, RT.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionSDL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipLE) & 7);
|
||||
auto mask = u64(0) - 1 >> shift;
|
||||
if(auto data = readDouble(address & ~7)) {
|
||||
writeDouble(address & ~7, *data & ~mask | RT.u64 >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSDR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipBE) & 7);
|
||||
auto mask = u64(0) - 1 << shift;
|
||||
if(auto data = readDouble(address & ~7)) {
|
||||
writeDouble(address & ~7, *data & ~mask | RT.u64 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSH() -> void {
|
||||
writeHalf(RS.u32 + IMMi16, RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLL() -> void {
|
||||
RD.u64 = i32(RT.u32 << SA);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLLV() -> void {
|
||||
RD.u64 = i32(RT.u32 << (RS.u32 & 31));
|
||||
}
|
||||
|
||||
auto CPU::instructionSLT() -> void {
|
||||
RD.u64 = RS.i64 < RT.i64;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTI() -> void {
|
||||
RT.u64 = RS.i64 < i64(IMMi16);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTIU() -> void {
|
||||
RT.u64 = RS.u64 < u64(IMMi16);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTU() -> void {
|
||||
RD.u64 = RS.u64 < RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionSRA() -> void {
|
||||
RD.u64 = RT.i32 >> SA;
|
||||
}
|
||||
|
||||
auto CPU::instructionSRAV() -> void {
|
||||
RD.u64 = RT.i32 >> (RS.u32 & 31);
|
||||
}
|
||||
|
||||
auto CPU::instructionSRL() -> void {
|
||||
RD.u64 = i32(RT.u32 >> SA);
|
||||
}
|
||||
|
||||
auto CPU::instructionSRLV() -> void {
|
||||
RD.u64 = i32(RT.u32 >> (RS.u32 & 31));
|
||||
}
|
||||
|
||||
auto CPU::instructionSUB() -> void {
|
||||
//if(RS.u32 < RT.u32) return exception.arithmeticOverflow();
|
||||
RD.u64 = i32(RS.u32 - RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSUBU() -> void {
|
||||
RD.u64 = i32(RS.u32 - RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSW() -> void {
|
||||
writeWord(RS.u32 + IMMi16, RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSWL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipLE) & 3);
|
||||
auto mask = u32(0) - 1 >> shift;
|
||||
if(auto data = readWord(address & ~3)) {
|
||||
writeWord(address & ~3, *data & ~mask | RT.u32 >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSWR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto shift = 8 * ((address ^ FlipBE) & 3);
|
||||
auto mask = u32(0) - 1 << shift;
|
||||
if(auto data = readWord(address & ~3)) {
|
||||
writeWord(address & ~3, *data & ~mask | RT.u32 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSYSCALL() -> void {
|
||||
exception.systemCall();
|
||||
}
|
||||
|
||||
auto CPU::instructionTEQ() -> void {
|
||||
if(RS.u64 == RT.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTEQI() -> void {
|
||||
if(RS.u64 == u64(IMMi16)) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGE() -> void {
|
||||
if(RS.i64 >= RT.i64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEI() -> void {
|
||||
if(RS.i64 >= i64(IMMi16)) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEIU() -> void {
|
||||
if(RS.u64 >= u64(IMMi16)) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEU() -> void {
|
||||
if(RS.u64 >= RT.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLT() -> void {
|
||||
if(RS.i64 < RT.i64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTI() -> void {
|
||||
if(RS.i64 < i64(IMMi16)) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTIU() -> void {
|
||||
if(RS.u64 < u64(IMMi16)) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTU() -> void {
|
||||
if(RS.u64 < RT.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTNE() -> void {
|
||||
if(RS.u64 != RT.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTNEI() -> void {
|
||||
if(RS.u64 != u64(IMMi16)) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionXOR() -> void {
|
||||
RD.u64 = RS.u64 ^ RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionXORI() -> void {
|
||||
RT.u64 = RS.u64 ^ IMMu16;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//{
|
||||
union cpu64 {
|
||||
struct { int32_t order_msb2(i32h, i32); };
|
||||
struct { uint32_t order_msb2(u32h, u32); };
|
||||
struct { int64_t i64; };
|
||||
struct { uint64_t u64; };
|
||||
auto i128() const -> int128_t { return (int128_t)i64; }
|
||||
auto u128() const -> uint128_t { return (uint128_t)u64; }
|
||||
};
|
||||
|
||||
struct Core {
|
||||
enum Register : uint {
|
||||
R0, //zero (read-only)
|
||||
AT, //assembler temporary
|
||||
V0, V1, //arithmetic values
|
||||
A0, A1, A2, A3, //subroutine parameters
|
||||
T0, T1, T2, T3, T4, T5, T6, T7, //temporary registers
|
||||
S0, S1, S2, S3, S4, S5, S6, S7, //saved registers
|
||||
T8, T9, //temporary registers
|
||||
K0, K1, //kernel registers
|
||||
GP, //global pointer
|
||||
SP, //stack pointer
|
||||
S8, //saved register
|
||||
RA, //return address
|
||||
};
|
||||
|
||||
cpu64 r[32];
|
||||
cpu64 lo;
|
||||
cpu64 hi;
|
||||
u64 pc; //program counter
|
||||
} core;
|
||||
|
||||
struct Branch {
|
||||
enum : u32 { Step, Take, DelaySlot, Exception, Discard };
|
||||
|
||||
auto inDelaySlot() const -> bool { return state == DelaySlot; }
|
||||
auto reset() -> void { state = Step; }
|
||||
auto take(u32 address) -> void { state = Take; pc = address; }
|
||||
auto delaySlot() -> void { state = DelaySlot; }
|
||||
auto exception() -> void { state = Exception; }
|
||||
auto discard() -> void { state = Discard; }
|
||||
|
||||
u64 pc;
|
||||
u32 state;
|
||||
} branch;
|
||||
|
||||
//cpu-instructions.cpp
|
||||
auto instructionADD() -> void;
|
||||
auto instructionADDI() -> void;
|
||||
auto instructionADDIU() -> void;
|
||||
auto instructionADDU() -> void;
|
||||
auto instructionAND() -> void;
|
||||
auto instructionANDI() -> void;
|
||||
auto instructionB(bool) -> void;
|
||||
auto instructionBAL(bool) -> void;
|
||||
auto instructionBALL(bool) -> void;
|
||||
auto instructionBL(bool) -> void;
|
||||
auto instructionBREAK() -> void;
|
||||
auto instructionCACHE() -> void;
|
||||
auto instructionDADD() -> void;
|
||||
auto instructionDADDI() -> void;
|
||||
auto instructionDADDIU() -> void;
|
||||
auto instructionDADDU() -> void;
|
||||
auto instructionDDIV() -> void;
|
||||
auto instructionDDIVU() -> void;
|
||||
auto instructionDIV() -> void;
|
||||
auto instructionDIVU() -> void;
|
||||
auto instructionDMULT() -> void;
|
||||
auto instructionDMULTU() -> void;
|
||||
auto instructionDSLL() -> void;
|
||||
auto instructionDSLL32() -> void;
|
||||
auto instructionDSLLV() -> void;
|
||||
auto instructionDSRA() -> void;
|
||||
auto instructionDSRA32() -> void;
|
||||
auto instructionDSRAV() -> void;
|
||||
auto instructionDSRL() -> void;
|
||||
auto instructionDSRL32() -> void;
|
||||
auto instructionDSRLV() -> void;
|
||||
auto instructionDSUB() -> void;
|
||||
auto instructionDSUBU() -> void;
|
||||
auto instructionJ() -> void;
|
||||
auto instructionJAL() -> void;
|
||||
auto instructionJALR() -> void;
|
||||
auto instructionJR() -> void;
|
||||
auto instructionLB() -> void;
|
||||
auto instructionLBU() -> void;
|
||||
auto instructionLD() -> void;
|
||||
auto instructionLDL() -> void;
|
||||
auto instructionLDR() -> void;
|
||||
auto instructionLH() -> void;
|
||||
auto instructionLHU() -> void;
|
||||
auto instructionLUI() -> void;
|
||||
auto instructionLL() -> void;
|
||||
auto instructionLLD() -> void;
|
||||
auto instructionLW() -> void;
|
||||
auto instructionLWL() -> void;
|
||||
auto instructionLWR() -> void;
|
||||
auto instructionLWU() -> void;
|
||||
auto instructionMFHI() -> void;
|
||||
auto instructionMFLO() -> void;
|
||||
auto instructionMTHI() -> void;
|
||||
auto instructionMTLO() -> void;
|
||||
auto instructionMULT() -> void;
|
||||
auto instructionMULTU() -> void;
|
||||
auto instructionNOR() -> void;
|
||||
auto instructionOR() -> void;
|
||||
auto instructionORI() -> void;
|
||||
auto instructionSB() -> void;
|
||||
auto instructionSC() -> void;
|
||||
auto instructionSD() -> void;
|
||||
auto instructionSCD() -> void;
|
||||
auto instructionSDL() -> void;
|
||||
auto instructionSDR() -> void;
|
||||
auto instructionSH() -> void;
|
||||
auto instructionSLL() -> void;
|
||||
auto instructionSLLV() -> void;
|
||||
auto instructionSLT() -> void;
|
||||
auto instructionSLTI() -> void;
|
||||
auto instructionSLTIU() -> void;
|
||||
auto instructionSLTU() -> void;
|
||||
auto instructionSRA() -> void;
|
||||
auto instructionSRAV() -> void;
|
||||
auto instructionSRL() -> void;
|
||||
auto instructionSRLV() -> void;
|
||||
auto instructionSUB() -> void;
|
||||
auto instructionSUBU() -> void;
|
||||
auto instructionSW() -> void;
|
||||
auto instructionSWL() -> void;
|
||||
auto instructionSWR() -> void;
|
||||
auto instructionSYSCALL() -> void;
|
||||
auto instructionTEQ() -> void;
|
||||
auto instructionTEQI() -> void;
|
||||
auto instructionTGE() -> void;
|
||||
auto instructionTGEI() -> void;
|
||||
auto instructionTGEIU() -> void;
|
||||
auto instructionTGEU() -> void;
|
||||
auto instructionTLT() -> void;
|
||||
auto instructionTLTI() -> void;
|
||||
auto instructionTLTIU() -> void;
|
||||
auto instructionTLTU() -> void;
|
||||
auto instructionTNE() -> void;
|
||||
auto instructionTNEI() -> void;
|
||||
auto instructionXOR() -> void;
|
||||
auto instructionXORI() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,475 @@
|
||||
auto CPU::Disassembler::disassemble(u32 address, u32 instruction) -> string {
|
||||
this->address = address;
|
||||
this->instruction = instruction;
|
||||
|
||||
auto v = EXECUTE();
|
||||
if(!v) v.append("invalid", string{"$", hex(instruction, 8L)});
|
||||
if(!instruction) v = {"nop"};
|
||||
auto s = pad(v.takeFirst(), -8L);
|
||||
return {s, v.merge(",")};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::EXECUTE() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto ftName = [&] { return fpuRegisterName (instruction >> 16 & 31); };
|
||||
auto ftValue = [&] { return fpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto imm16i = [&] { return immediate(i16(instruction)); };
|
||||
auto imm16u = [&] { return immediate(u16(instruction), 16L); };
|
||||
auto jump = [&] { return immediate(address + 4 & 0xf000'0000 | (instruction & 0x03ff'ffff) << 2); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
auto offset = [&] { return cpuRegisterIndex(instruction >> 21 & 31, i16(instruction)); };
|
||||
|
||||
auto ALU = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), rsValue(), immediate(u16(instruction))};
|
||||
};
|
||||
|
||||
auto ADDI = [&](string_view add, string_view sub, string_view mov) -> vector<string> {
|
||||
if(!(instruction >> 21 & 31)) return {mov, rtName(), immediate(i16(instruction), 32L)};
|
||||
return {i16(instruction) >= 0 ? add : sub, rtName(), rsValue(), immediate(abs(i16(instruction)))};
|
||||
};
|
||||
|
||||
auto BRANCH1 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
auto BRANCH2 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), rtValue(), branch()};
|
||||
};
|
||||
|
||||
auto CACHE = [&](string_view name) -> vector<string> {
|
||||
auto cache = instruction >> 16 & 3;
|
||||
auto op = instruction >> 18 & 7;
|
||||
string type = "reserved";
|
||||
if(cache == 0) switch(op) {
|
||||
case 0: type = "code(IndexInvalidate)"; break;
|
||||
case 1: type = "code(IndexLoadTag)"; break;
|
||||
case 2: type = "code(IndexStoreTag)"; break;
|
||||
case 4: type = "code(HitInvalidate)"; break;
|
||||
case 5: type = "code(Fill)"; break;
|
||||
case 6: type = "code(HitWriteBack)"; break;
|
||||
}
|
||||
if(cache == 1) switch(op) {
|
||||
case 0: type = "data(IndexWriteBackInvalidate)"; break;
|
||||
case 1: type = "data(IndexLoadTag)"; break;
|
||||
case 2: type = "data(IndexStoreTag)"; break;
|
||||
case 3: type = "data(CreateDirtyExclusive)"; break;
|
||||
case 4: type = "data(HitInvalidate)"; break;
|
||||
case 5: type = "data(HitWriteBackInvalidate)"; break;
|
||||
case 6: type = "data(HitWriteBack)"; break;
|
||||
}
|
||||
return {name, type, offset()};
|
||||
};
|
||||
|
||||
auto JUMP = [&](string_view name) -> vector<string> {
|
||||
return {name, jump()};
|
||||
};
|
||||
|
||||
auto LOAD = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), offset()};
|
||||
};
|
||||
|
||||
auto STORE = [&](string_view name) -> vector<string> {
|
||||
return {name, rtValue(), offset()};
|
||||
};
|
||||
|
||||
switch(instruction >> 26) {
|
||||
case 0x00: return SPECIAL();
|
||||
case 0x01: return REGIMM();
|
||||
case 0x02: return JUMP("j");
|
||||
case 0x03: return JUMP("jal");
|
||||
case 0x04: return BRANCH2("beq");
|
||||
case 0x05: return BRANCH2("bne");
|
||||
case 0x06: return BRANCH1("blez");
|
||||
case 0x07: return BRANCH1("bgtz");
|
||||
case 0x08: return ADDI("addi", "subi", "li");
|
||||
case 0x09: return ADDI("addiu", "subiu", "liu");
|
||||
case 0x0a: return ALU("slti");
|
||||
case 0x0b: return ALU("sltiu");
|
||||
case 0x0c: return ALU("andi");
|
||||
case 0x0d: return ALU("ori");
|
||||
case 0x0e: return ALU("xori");
|
||||
case 0x0f: return {"lui", rtName(), imm16u()};
|
||||
case 0x10: return SCC();
|
||||
case 0x11: return FPU();
|
||||
case 0x12: break; //COP2
|
||||
case 0x13: break; //COP3
|
||||
case 0x14: return BRANCH2("beql");
|
||||
case 0x15: return BRANCH2("bnel");
|
||||
case 0x16: return BRANCH1("blezl");
|
||||
case 0x17: return BRANCH1("bgtzl");
|
||||
case 0x18: return ADDI("daddi", "dsubi", "dli");
|
||||
case 0x19: return ADDI("daddiu", "dsubiu", "dliu");
|
||||
case 0x1a: return LOAD("ldl");
|
||||
case 0x1b: return LOAD("ldr");
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return LOAD("lb");
|
||||
case 0x21: return LOAD("lh");
|
||||
case 0x22: return LOAD("lwl");
|
||||
case 0x23: return LOAD("lw");
|
||||
case 0x24: return LOAD("lbu");
|
||||
case 0x25: return LOAD("lhu");
|
||||
case 0x26: return LOAD("lwr");
|
||||
case 0x27: return LOAD("lwu");
|
||||
case 0x28: return STORE("sb");
|
||||
case 0x29: return STORE("sh");
|
||||
case 0x2a: return STORE("swl");
|
||||
case 0x2b: return STORE("sw");
|
||||
case 0x2c: return STORE("sdl");
|
||||
case 0x2d: return STORE("sdr");
|
||||
case 0x2e: return STORE("swr");
|
||||
case 0x2f: return CACHE("cache");
|
||||
case 0x30: return LOAD("ll");
|
||||
case 0x31: return {"lwc1", ftName(), offset()};
|
||||
case 0x32: break; //LWC2
|
||||
case 0x33: break; //LWC3
|
||||
case 0x34: return LOAD("lld");
|
||||
case 0x35: return {"ldc1", ftName(), offset()};
|
||||
case 0x36: break; //LDC2
|
||||
case 0x37: return LOAD("ld");
|
||||
case 0x38: return STORE("sc");
|
||||
case 0x39: return {"swc1", ftValue(), offset()};
|
||||
case 0x3a: break; //SWC2
|
||||
case 0x3b: break; //SWC3
|
||||
case 0x3c: return STORE("scd");
|
||||
case 0x3d: return {"sdc1", ftValue(), offset()};
|
||||
case 0x3e: break; //SDC2
|
||||
case 0x3f: return STORE("sd");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::SPECIAL() -> vector<string> {
|
||||
auto shift = [&] { return string{instruction >> 6 & 31}; };
|
||||
auto rdName = [&] { return cpuRegisterName (instruction >> 11 & 31); };
|
||||
auto rdValue = [&] { return cpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
|
||||
auto ALU = [&](string_view name, string_view by) -> vector<string> {
|
||||
return {name, rdName(), rtValue(), by};
|
||||
};
|
||||
|
||||
auto JALR = [&](string_view name) -> vector<string> {
|
||||
if((instruction >> 11 & 31) == 31) return {name, rsValue()};
|
||||
return {name, rdName(), rsValue()};
|
||||
};
|
||||
|
||||
auto REG = [&](string_view name) -> vector<string> {
|
||||
return {name, rdName(), rsValue(), rtValue()};
|
||||
};
|
||||
|
||||
auto ST = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), rtValue()};
|
||||
};
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return ALU("sll", shift());
|
||||
case 0x01: break;
|
||||
case 0x02: return ALU("srl", shift());
|
||||
case 0x03: return ALU("sra", shift());
|
||||
case 0x04: return ALU("sllv", rsValue());
|
||||
case 0x05: break;
|
||||
case 0x06: return ALU("srlv", rsValue());
|
||||
case 0x07: return ALU("srav", rsValue());
|
||||
case 0x08: return {"jr", rsValue()};
|
||||
case 0x09: return JALR("jalr");
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: return {"syscall"};
|
||||
case 0x0d: return {"break"};
|
||||
case 0x0e: break;
|
||||
case 0x0f: return {"sync"};
|
||||
case 0x10: return {"mfhi", rdName(), {"hi", hint("{$", hex(self.core.hi.u64, 8L), "}")}};
|
||||
case 0x11: return {"mthi", rsValue(), "hi"};
|
||||
case 0x12: return {"mflo", rdName(), {"lo", hint("{$", hex(self.core.lo.u64, 8L), "}")}};
|
||||
case 0x13: return {"mtlo", rsValue(), "lo"};
|
||||
case 0x14: return ALU("dsllv", rsValue());
|
||||
case 0x15: break;
|
||||
case 0x16: return ALU("dsrlv", rsValue());
|
||||
case 0x17: return ALU("dsrav", rsValue());
|
||||
case 0x18: return ST("mult");
|
||||
case 0x19: return ST("multu");
|
||||
case 0x1a: return ST("div");
|
||||
case 0x1b: return ST("divu");
|
||||
case 0x1c: return ST("dmult");
|
||||
case 0x1d: return ST("dmultu");
|
||||
case 0x1e: return ST("ddiv");
|
||||
case 0x1f: return ST("ddivu");
|
||||
case 0x20: return REG("add");
|
||||
case 0x21: return REG("addu");
|
||||
case 0x22: return REG("sub");
|
||||
case 0x23: return REG("subu");
|
||||
case 0x24: return REG("and");
|
||||
case 0x25: return REG("or");
|
||||
case 0x26: return REG("xor");
|
||||
case 0x27: return REG("nor");
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return REG("slt");
|
||||
case 0x2b: return REG("sltu");
|
||||
case 0x2c: return REG("dadd");
|
||||
case 0x2d: return REG("daddu");
|
||||
case 0x2e: return REG("dsub");
|
||||
case 0x2f: return REG("dsubu");
|
||||
case 0x30: return ST("tge");
|
||||
case 0x31: return ST("tgeu");
|
||||
case 0x32: return ST("tlt");
|
||||
case 0x33: return ST("tltu");
|
||||
case 0x34: return ST("teq");
|
||||
case 0x35: break;
|
||||
case 0x36: return ST("tne");
|
||||
case 0x37: break;
|
||||
case 0x38: return ALU("dsll", shift());
|
||||
case 0x39: break;
|
||||
case 0x3a: return ALU("dsrl", shift());
|
||||
case 0x3b: return ALU("dsra", shift());
|
||||
case 0x3c: return ALU("dsll32", shift());
|
||||
case 0x3d: break;
|
||||
case 0x3e: return ALU("dsrl32", shift());
|
||||
case 0x3f: return ALU("dsra32", shift());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::REGIMM() -> vector<string> {
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto imm16i = [&] { return immediate(i16(instruction)); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
auto BRANCH = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
auto TRAP = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), imm16i()};
|
||||
};
|
||||
|
||||
switch(instruction >> 16 & 0x1f) {
|
||||
case 0x00: return BRANCH("bltz");
|
||||
case 0x01: return BRANCH("bgez");
|
||||
case 0x02: return BRANCH("bltzl");
|
||||
case 0x03: return BRANCH("bgezl");
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: return TRAP("tgei");
|
||||
case 0x09: return TRAP("tgeiu");
|
||||
case 0x0a: return TRAP("tlti");
|
||||
case 0x0b: return TRAP("tltiu");
|
||||
case 0x0c: return TRAP("teqi");
|
||||
case 0x0d: break;
|
||||
case 0x0e: return TRAP("tnei");
|
||||
case 0x0f: break;
|
||||
case 0x10: return BRANCH("bltzal");
|
||||
case 0x11: return BRANCH("bgezal");
|
||||
case 0x12: return BRANCH("bltzall");
|
||||
case 0x13: return BRANCH("bgezall");
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::SCC() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto sdName = [&] { return sccRegisterName (instruction >> 11 & 31); };
|
||||
auto sdValue = [&] { return sccRegisterValue(instruction >> 11 & 31); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
case 0x00: return {"mfc0", rtName(), sdValue()};
|
||||
case 0x01: return {"dmfc0", rtName(), sdValue()};
|
||||
case 0x02: break; //CFC0
|
||||
case 0x04: return {"mtc0", rtValue(), sdName()};
|
||||
case 0x05: return {"dmtc0", rtValue(), sdName()};
|
||||
case 0x06: break; //CTC0
|
||||
case 0x08: break; //BC0
|
||||
}
|
||||
if(!(instruction >> 25 & 1)) return {};
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x01: return {"tlbr"};
|
||||
case 0x02: return {"tlbwi"};
|
||||
case 0x06: return {"tlbwr"};
|
||||
case 0x08: return {"tlbp"};
|
||||
case 0x18: return {"eret"};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::FPU() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rdName = [&] { return fpuRegisterName (instruction >> 11 & 31); };
|
||||
auto rdValue = [&] { return fpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto cdName = [&] { return ccrRegisterName (instruction >> 11 & 31); };
|
||||
auto cdValue = [&] { return ccrRegisterValue(instruction >> 11 & 31); }; //todo
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
case 0x00: return {"mfc1", rtName(), rdValue()};
|
||||
case 0x01: return {"dmfc1", rtName(), rdValue()};
|
||||
case 0x02: return {"cfc1", rtName(), cdValue()};
|
||||
case 0x04: return {"mtc1", rtValue(), rdName()};
|
||||
case 0x05: return {"dmtc1", rtValue(), rdName()};
|
||||
case 0x06: return {"ctc1", rtValue(), cdName()};
|
||||
case 0x08: switch(instruction >> 16 & 3) {
|
||||
case 0x00: return {"bc1f", branch()};
|
||||
case 0x01: return {"bc1t", branch()};
|
||||
case 0x02: return {"bc1fl", branch()};
|
||||
case 0x03: return {"bc1tl", branch()};
|
||||
}
|
||||
}
|
||||
if(!(instruction >> 25 & 1)) return {};
|
||||
|
||||
auto fdName = [&] { return fpuRegisterName (instruction >> 6 & 31); };
|
||||
auto fsValue = [&] { return fpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto ftValue = [&] { return fpuRegisterValue(instruction >> 16 & 31); };
|
||||
|
||||
auto DS = [&](string_view name) -> vector<string> {
|
||||
return {name, fdName(), fsValue()};
|
||||
};
|
||||
|
||||
auto DST = [&](string_view name) -> vector<string> {
|
||||
return {name, fdName(), fsValue(), ftValue()};
|
||||
};
|
||||
|
||||
auto ST = [&](string_view name) -> vector<string> {
|
||||
return {name, fsValue(), ftValue()};
|
||||
};
|
||||
|
||||
bool s = (instruction & 1 << 21) == 0;
|
||||
bool i = (instruction & 1 << 23) != 0;
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return DST(s ? "adds" : "addd" );
|
||||
case 0x01: return DST(s ? "subs" : "subd" );
|
||||
case 0x02: return DST(s ? "muls" : "muld" );
|
||||
case 0x03: return DST(s ? "divs" : "divd" );
|
||||
case 0x04: return DS (s ? "sqrts" : "sqrtd" );
|
||||
case 0x05: return DS (s ? "abss" : "absd" );
|
||||
case 0x06: return DS (s ? "movs" : "movd" );
|
||||
case 0x07: return DS (s ? "negs" : "negd" );
|
||||
case 0x08: return DS (s ? "roundls" : "roundld");
|
||||
case 0x09: return DS (s ? "truncls" : "truncld");
|
||||
case 0x0a: return DS (s ? "ceills" : "ceilld" );
|
||||
case 0x0b: return DS (s ? "floorls" : "floorld");
|
||||
case 0x0c: return DS (s ? "roundws" : "roundwd");
|
||||
case 0x0d: return DS (s ? "truncws" : "truncwd");
|
||||
case 0x0e: return DS (s ? "ceilws" : "ceilwd" );
|
||||
case 0x0f: return DS (s ? "floorws" : "floorwd");
|
||||
case 0x20: return DS (i ? (s ? "cvtsw" : "cvtsl") : "cvtsd");
|
||||
case 0x21: return DS (i ? (s ? "cvtdw" : "cvtdl") : "cvtds");
|
||||
case 0x24: return DS (s ? "cvtws" : "cvtwd");
|
||||
case 0x25: return DS (s ? "cvtls" : "cvtld");
|
||||
case 0x30: case 0x38: return ST(s ? "cfs" : "cfd" );
|
||||
case 0x31: case 0x39: return ST(s ? "cuns" : "cund" );
|
||||
case 0x32: case 0x3a: return ST(s ? "ceqs" : "ceqd" );
|
||||
case 0x33: case 0x3b: return ST(s ? "cueqs" : "cueqd");
|
||||
case 0x34: case 0x3c: return ST(s ? "colts" : "coltd");
|
||||
case 0x35: case 0x3d: return ST(s ? "cults" : "cultd");
|
||||
case 0x36: case 0x3e: return ST(s ? "coles" : "coled");
|
||||
case 0x37: case 0x3f: return ST(s ? "cules" : "culed");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::immediate(i64 value, uint bits) const -> string {
|
||||
if(value < 0) return {"-$", hex(-value, bits >> 2)};
|
||||
return {"$", hex(value, bits >> 2)};
|
||||
};
|
||||
|
||||
auto CPU::Disassembler::cpuRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
||||
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
||||
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::cpuRegisterValue(uint index) const -> string {
|
||||
if(index && showValues) return {cpuRegisterName(index), hint("{$", hex(self.core.r[index].u64, 8L), "}")};
|
||||
return cpuRegisterName(index);
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::cpuRegisterIndex(uint index, i16 offset) const -> string {
|
||||
string adjust;
|
||||
if(offset >= 0) adjust = {"+$", hex( offset)};
|
||||
if(offset < 0) adjust = {"-$", hex(-offset)};
|
||||
if(index && showValues) return {cpuRegisterName(index), adjust, hint("{$", hex(self.core.r[index].u64 + offset, 8L), "}")};
|
||||
return {cpuRegisterName(index), adjust};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::sccRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"Index", "Random", "EntryLo0", "EntryLo1",
|
||||
"Context", "PageMask", "Wired", "Unused7",
|
||||
"BadVAddr", "Count", "EntryHi", "Compare",
|
||||
"Status", "Cause", "EPC", "PrID",
|
||||
"Config", "LLAddr", "WatchLo", "WatchHi",
|
||||
"XContext", "Unused21", "Unused22", "Unused23",
|
||||
"Unused24", "Unused25", "ParityError", "CacheError",
|
||||
"TagLo", "TagHi", "ErrorEPC", "Unused31",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::sccRegisterValue(uint index) const -> string {
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index)), "}")};
|
||||
return sccRegisterName(index);
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::fpuRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||||
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
|
||||
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
|
||||
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::fpuRegisterValue(uint index) const -> string {
|
||||
bool f32 = (instruction & 1 << 21) == 0;
|
||||
bool f64 = (instruction & 1 << 21) != 0;
|
||||
if(f32 && showValues) return {fpuRegisterName(index), hint("{", self.fpu.r[index].f32, "}")};
|
||||
if(f64 && showValues) return {fpuRegisterName(index), hint("{", self.fpu.r[index].f64, "}")};
|
||||
return fpuRegisterName(index);
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::ccrRegisterName(uint index) const -> string {
|
||||
return {"ccr", index};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::ccrRegisterValue(uint index) const -> string {
|
||||
if(showValues) return {ccrRegisterName(index), hint("{$", hex(self.getControlRegisterFPU(index)), "}")};
|
||||
return ccrRegisterName(index);
|
||||
}
|
||||
|
||||
template<typename... P>
|
||||
auto CPU::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//{
|
||||
struct Disassembler {
|
||||
CPU& self;
|
||||
Disassembler(CPU& self) : self(self) {}
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(u32 address, u32 instruction) -> string;
|
||||
template<typename... P> auto hint(P&&... p) const -> string;
|
||||
|
||||
bool showColors = true;
|
||||
bool showValues = true;
|
||||
|
||||
private:
|
||||
auto EXECUTE() -> vector<string>;
|
||||
auto SPECIAL() -> vector<string>;
|
||||
auto REGIMM() -> vector<string>;
|
||||
auto SCC() -> vector<string>;
|
||||
auto FPU() -> vector<string>;
|
||||
auto immediate(i64 value, uint bits = 0) const -> string;
|
||||
auto cpuRegisterName(uint index) const -> string;
|
||||
auto cpuRegisterValue(uint index) const -> string;
|
||||
auto cpuRegisterIndex(uint index, i16 offset) const -> string;
|
||||
auto sccRegisterName(uint index) const -> string;
|
||||
auto sccRegisterValue(uint index) const -> string;
|
||||
auto fpuRegisterName(uint index) const -> string;
|
||||
auto fpuRegisterValue(uint index) const -> string;
|
||||
auto ccrRegisterName(uint index) const -> string;
|
||||
auto ccrRegisterValue(uint index) const -> string;
|
||||
|
||||
u32 address;
|
||||
u32 instruction;
|
||||
} disassembler{*this};
|
||||
//};
|
||||
@@ -0,0 +1,23 @@
|
||||
//{
|
||||
struct Exception {
|
||||
CPU& self;
|
||||
auto tlbModification() { self.raiseException(1); }
|
||||
auto tlbLoad() { self.raiseException(2); }
|
||||
auto tlbStore() { self.raiseException(3); }
|
||||
auto addressLoad() { self.raiseException(4); }
|
||||
auto addressStore() { self.raiseException(5); }
|
||||
auto busInstruction() { self.raiseException(6); }
|
||||
auto busData() { self.raiseException(7); }
|
||||
auto systemCall() { self.raiseException(8); }
|
||||
auto breakpoint() { self.raiseException(9); }
|
||||
auto reservedInstruction() { self.raiseException(10); }
|
||||
auto coprocessor0() { self.raiseException(11, 0); }
|
||||
auto coprocessor1() { self.raiseException(11, 1); }
|
||||
auto coprocessor2() { self.raiseException(11, 2); }
|
||||
auto coprocessor3() { self.raiseException(11, 3); }
|
||||
auto arithmeticOverflow() { self.raiseException(12); }
|
||||
auto trap() { self.raiseException(13); }
|
||||
auto floatingPoint() { self.raiseException(15); }
|
||||
auto watchAddress() { self.raiseException(23); }
|
||||
} exception{*this};
|
||||
//};
|
||||
@@ -0,0 +1,300 @@
|
||||
#define CF fpu.csr.compare
|
||||
#define FD(type) fgr<type>(OP >> 6 & 31)
|
||||
#define FS(type) fgr<type>(OP >> 11 & 31)
|
||||
#define FT(type) fgr<type>(OP >> 16 & 31)
|
||||
#define FPU_SINGLE ((OP & 1 << 21) == 0)
|
||||
#define FPU_DOUBLE ((OP & 1 << 21) != 0)
|
||||
#define FPU_REAL ((OP & 1 << 23) == 0)
|
||||
#define FPU_INTEGER ((OP & 1 << 23) != 0)
|
||||
|
||||
auto CPU::instructionBC1() -> void {
|
||||
bool condition = OP >> 16 & 1;
|
||||
bool likely = OP >> 17 & 1;
|
||||
if(CF == condition) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
else if(likely) branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionCFC1() -> void {
|
||||
RT.u64 = i32(getControlRegisterFPU(RDn));
|
||||
}
|
||||
|
||||
auto CPU::instructionCTC1() -> void {
|
||||
setControlRegisterFPU(RDn, RT.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMFC1() -> void {
|
||||
RT.u64 = FS(u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMTC1() -> void {
|
||||
FS(u64) = RT.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionFABS() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = fabs(FS(f32));
|
||||
} else {
|
||||
FD(f64) = fabs(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFADD() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = FS(f32) + FT(f32);
|
||||
} else {
|
||||
FD(f64) = FS(f64) + FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCEILL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i64) = ceil(FS(f32));
|
||||
} else {
|
||||
FD(i64) = ceil(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCEILW() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i32) = ceil(FS(f32));
|
||||
} else {
|
||||
FD(i32) = ceil(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCEQ() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) == FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) == FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCF() -> void {
|
||||
CF = 0;
|
||||
}
|
||||
|
||||
auto CPU::instructionFCOLE() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) <= FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) <= FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCOLT() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) < FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) < FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCUEQ() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) == FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) == FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCULE() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) <= FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) <= FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCULT() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) < FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) < FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCUN() -> void {
|
||||
CF = 0;
|
||||
}
|
||||
|
||||
auto CPU::instructionFCVTS() -> void {
|
||||
if(FPU_REAL) {
|
||||
FD(f32) = FS(f64);
|
||||
} else if(FPU_SINGLE) {
|
||||
FD(f32) = FS(i32);
|
||||
} else {
|
||||
FD(f32) = FS(i64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCVTD() -> void {
|
||||
if(FPU_REAL) {
|
||||
FD(f64) = FS(f32);
|
||||
} else if(FPU_SINGLE) {
|
||||
FD(f64) = FS(i32);
|
||||
} else {
|
||||
FD(f64) = FS(i64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCVTL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i64) = FS(f32);
|
||||
} else {
|
||||
FD(i64) = FS(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFCVTW() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i32) = FS(f32);
|
||||
} else {
|
||||
FD(i32) = FS(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFDIV() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
if(FT(f32)) {
|
||||
FD(f32) = FS(f32) / FT(f32);
|
||||
} else if(fpu.csr.enable.divisionByZero) {
|
||||
fpu.csr.flag.divisionByZero = 1;
|
||||
fpu.csr.cause.divisionByZero = 1;
|
||||
exception.floatingPoint();
|
||||
}
|
||||
} else {
|
||||
if(FT(f64)) {
|
||||
FD(f64) = FS(f64) / FT(f64);
|
||||
} else if(fpu.csr.enable.divisionByZero) {
|
||||
fpu.csr.flag.divisionByZero = 1;
|
||||
fpu.csr.cause.divisionByZero = 1;
|
||||
exception.floatingPoint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFFLOORL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i64) = floor(FS(f32));
|
||||
} else {
|
||||
FD(i64) = floor(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFFLOORW() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i32) = floor(FS(f32));
|
||||
} else {
|
||||
FD(i32) = floor(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFMOV() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = FS(f32);
|
||||
} else {
|
||||
FD(f64) = FS(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFMUL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = FS(f32) * FT(f32);
|
||||
} else {
|
||||
FD(f64) = FS(f64) * FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFNEG() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = -FS(f32);
|
||||
} else {
|
||||
FD(f64) = -FS(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFROUNDL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i64) = nearbyint(FS(f32));
|
||||
} else {
|
||||
FD(i64) = nearbyint(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFROUNDW() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i32) = nearbyint(FS(f32));
|
||||
} else {
|
||||
FD(i32) = nearbyint(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFSQRT() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = sqrt(FS(f32));
|
||||
} else {
|
||||
FD(f64) = sqrt(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFSUB() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = FS(f32) - FT(f32);
|
||||
} else {
|
||||
FD(f64) = FS(f64) - FT(f64);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFTRUNCL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i64) = FS(f32) < 0 ? ceil(FS(f32)) : floor(FS(f32));
|
||||
} else {
|
||||
FD(i64) = FS(f64) < 0 ? ceil(FS(f64)) : floor(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFTRUNCW() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i32) = FS(f32) < 0 ? ceil(FS(f32)) : floor(FS(f32));
|
||||
} else {
|
||||
FD(i32) = FS(f64) < 0 ? ceil(FS(f64)) : floor(FS(f64));
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLDC1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
if(auto data = readDouble(RS.u32 + IMMi16)) FT(u64) = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionLWC1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) FT(u32) = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionMFC1() -> void {
|
||||
RT.u64 = FS(i32);
|
||||
}
|
||||
|
||||
auto CPU::instructionMTC1() -> void {
|
||||
FS(i32) = RT.u32;
|
||||
}
|
||||
|
||||
auto CPU::instructionSDC1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
writeDouble(RS.u32 + IMMi16, FT(u64));
|
||||
}
|
||||
|
||||
auto CPU::instructionSWC1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
writeWord(RS.u32 + IMMi16, FT(u32));
|
||||
}
|
||||
|
||||
#undef CF
|
||||
#undef FD
|
||||
#undef FS
|
||||
#undef FT
|
||||
#undef FPU_SINGLE
|
||||
#undef FPU_DOUBLE
|
||||
#undef FPU_REAL
|
||||
#undef FPU_INTEGER
|
||||
@@ -0,0 +1,118 @@
|
||||
template<> auto CPU::fgr<i32>(uint index) -> i32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].i32;
|
||||
} else if(index & 1) {
|
||||
return fpu.r[index & ~1].i32h;
|
||||
} else {
|
||||
return fpu.r[index & ~1].i32;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<u32>(uint index) -> u32& {
|
||||
return (u32&)fgr<i32>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<f32>(uint index) -> f32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].f32;
|
||||
} else if(index & 1) {
|
||||
return fpu.r[index & ~1].f32h;
|
||||
} else {
|
||||
return fpu.r[index & ~1].f32;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<i64>(uint index) -> i64& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].i64;
|
||||
} else {
|
||||
return fpu.r[index & ~1].i64;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<u64>(uint index) -> u64& {
|
||||
return (u64&)fgr<i64>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<f64>(uint index) -> f64& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].f64;
|
||||
} else {
|
||||
return fpu.r[index & ~1].f64;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::getControlRegisterFPU(uint5 index) -> u32 {
|
||||
uint32 data;
|
||||
switch(index) {
|
||||
case 0: //coprocessor revision identifier
|
||||
data.bit(0, 7) = fpu.coprocessor.revision;
|
||||
data.bit(8,15) = fpu.coprocessor.implementation;
|
||||
break;
|
||||
case 31: //control / status register
|
||||
data.bit( 0) = fpu.csr.roundMode.bit(0);
|
||||
data.bit( 1) = fpu.csr.roundMode.bit(1);
|
||||
data.bit( 2) = fpu.csr.flag.inexact;
|
||||
data.bit( 3) = fpu.csr.flag.underflow;
|
||||
data.bit( 4) = fpu.csr.flag.overflow;
|
||||
data.bit( 5) = fpu.csr.flag.divisionByZero;
|
||||
data.bit( 6) = fpu.csr.flag.invalidOperation;
|
||||
data.bit( 7) = fpu.csr.enable.inexact;
|
||||
data.bit( 8) = fpu.csr.enable.underflow;
|
||||
data.bit( 9) = fpu.csr.enable.overflow;
|
||||
data.bit(10) = fpu.csr.enable.divisionByZero;
|
||||
data.bit(11) = fpu.csr.enable.invalidOperation;
|
||||
data.bit(12) = fpu.csr.cause.inexact;
|
||||
data.bit(13) = fpu.csr.cause.underflow;
|
||||
data.bit(14) = fpu.csr.cause.overflow;
|
||||
data.bit(15) = fpu.csr.cause.divisionByZero;
|
||||
data.bit(16) = fpu.csr.cause.invalidOperation;
|
||||
data.bit(17) = fpu.csr.cause.unimplementedOperation;
|
||||
data.bit(23) = fpu.csr.compare;
|
||||
data.bit(24) = fpu.csr.flushed;
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::setControlRegisterFPU(uint5 index, uint32 data) -> void {
|
||||
//read-only variables are defined but commented out for documentation purposes
|
||||
switch(index) {
|
||||
case 0: //coprocessor revision identifier
|
||||
//fpu.coprocessor.revision = data.bit(0, 7);
|
||||
//fpu.coprocessor.implementation = data.bit(8,15);
|
||||
break;
|
||||
case 31: {//control / status register
|
||||
uint roundModePrevious = fpu.csr.roundMode;
|
||||
fpu.csr.roundMode.bit(0) = data.bit( 0);
|
||||
fpu.csr.roundMode.bit(1) = data.bit( 1);
|
||||
fpu.csr.flag.inexact = data.bit( 2);
|
||||
fpu.csr.flag.underflow = data.bit( 3);
|
||||
fpu.csr.flag.overflow = data.bit( 4);
|
||||
fpu.csr.flag.divisionByZero = data.bit( 5);
|
||||
fpu.csr.flag.invalidOperation = data.bit( 6);
|
||||
fpu.csr.enable.inexact = data.bit( 7);
|
||||
fpu.csr.enable.underflow = data.bit( 8);
|
||||
fpu.csr.enable.overflow = data.bit( 9);
|
||||
fpu.csr.enable.divisionByZero = data.bit(10);
|
||||
fpu.csr.enable.invalidOperation = data.bit(11);
|
||||
fpu.csr.cause.inexact = data.bit(12);
|
||||
fpu.csr.cause.underflow = data.bit(13);
|
||||
fpu.csr.cause.overflow = data.bit(14);
|
||||
fpu.csr.cause.divisionByZero = data.bit(15);
|
||||
fpu.csr.cause.invalidOperation = data.bit(16);
|
||||
fpu.csr.cause.unimplementedOperation = data.bit(17);
|
||||
fpu.csr.compare = data.bit(23);
|
||||
fpu.csr.flushed = data.bit(24);
|
||||
|
||||
if(fpu.csr.roundMode != roundModePrevious) {
|
||||
switch(fpu.csr.roundMode) {
|
||||
case 0: fesetround(FE_TONEAREST); break;
|
||||
case 1: fesetround(FE_TOWARDZERO); break;
|
||||
case 2: fesetround(FE_UPWARD); break;
|
||||
case 3: fesetround(FE_DOWNWARD); break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//{
|
||||
//fpu-registers.cpp
|
||||
template<typename T> auto fgr(uint) -> T&;
|
||||
auto getControlRegisterFPU(uint5) -> u32;
|
||||
auto setControlRegisterFPU(uint5, uint32) -> void;
|
||||
|
||||
union fpu64 {
|
||||
struct { int32_t order_msb2(i32h, i32); };
|
||||
struct { uint32_t order_msb2(u32h, u32); };
|
||||
struct { float32_t order_msb2(f32h, f32); };
|
||||
struct { int64_t i64; };
|
||||
struct { uint64_t u64; };
|
||||
struct { float64_t f64; };
|
||||
};
|
||||
|
||||
struct FPU {
|
||||
fpu64 r[32];
|
||||
|
||||
struct Coprocessor {
|
||||
static constexpr u8 revision = 0x00;
|
||||
static constexpr u8 implementation = 0x0b;
|
||||
} coprocessor;
|
||||
|
||||
struct ControlStatus {
|
||||
uint2 roundMode = 0;
|
||||
struct Flag {
|
||||
uint1 inexact = 0;
|
||||
uint1 underflow = 0;
|
||||
uint1 overflow = 0;
|
||||
uint1 divisionByZero = 0;
|
||||
uint1 invalidOperation = 0;
|
||||
} flag;
|
||||
struct Enable {
|
||||
uint1 inexact = 0;
|
||||
uint1 underflow = 0;
|
||||
uint1 overflow = 0;
|
||||
uint1 divisionByZero = 0;
|
||||
uint1 invalidOperation = 0;
|
||||
} enable;
|
||||
struct Cause {
|
||||
uint1 inexact = 0;
|
||||
uint1 underflow = 0;
|
||||
uint1 overflow = 0;
|
||||
uint1 divisionByZero = 0;
|
||||
uint1 invalidOperation = 0;
|
||||
uint1 unimplementedOperation = 0;
|
||||
} cause;
|
||||
uint1 compare = 0;
|
||||
uint1 flushed = 0;
|
||||
} csr;
|
||||
} fpu;
|
||||
|
||||
//fpu-instructions.cpp
|
||||
auto instructionBC1() -> void;
|
||||
auto instructionCFC1() -> void;
|
||||
auto instructionCTC1() -> void;
|
||||
auto instructionDMFC1() -> void;
|
||||
auto instructionDMTC1() -> void;
|
||||
auto instructionFABS() -> void;
|
||||
auto instructionFADD() -> void;
|
||||
auto instructionFCEILL() -> void;
|
||||
auto instructionFCEILW() -> void;
|
||||
auto instructionFCEQ() -> void;
|
||||
auto instructionFCF() -> void;
|
||||
auto instructionFCOLE() -> void;
|
||||
auto instructionFCOLT() -> void;
|
||||
auto instructionFCUEQ() -> void;
|
||||
auto instructionFCULE() -> void;
|
||||
auto instructionFCULT() -> void;
|
||||
auto instructionFCUN() -> void;
|
||||
auto instructionFCVTS() -> void;
|
||||
auto instructionFCVTD() -> void;
|
||||
auto instructionFCVTL() -> void;
|
||||
auto instructionFCVTW() -> void;
|
||||
auto instructionFDIV() -> void;
|
||||
auto instructionFFLOORL() -> void;
|
||||
auto instructionFFLOORW() -> void;
|
||||
auto instructionFMOV() -> void;
|
||||
auto instructionFMUL() -> void;
|
||||
auto instructionFNEG() -> void;
|
||||
auto instructionFROUNDL() -> void;
|
||||
auto instructionFROUNDW() -> void;
|
||||
auto instructionFSQRT() -> void;
|
||||
auto instructionFSUB() -> void;
|
||||
auto instructionFTRUNCL() -> void;
|
||||
auto instructionFTRUNCW() -> void;
|
||||
auto instructionLDC1() -> void;
|
||||
auto instructionLWC1() -> void;
|
||||
auto instructionMFC1() -> void;
|
||||
auto instructionMTC1() -> void;
|
||||
auto instructionSDC1() -> void;
|
||||
auto instructionSWC1() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,317 @@
|
||||
auto CPU::raiseException(uint code, uint coprocessor) -> void {
|
||||
if(debugger.tracer.exception->enabled()) {
|
||||
if(code != 0) debugger.exception(hex(code, 2L));
|
||||
}
|
||||
|
||||
if(!scc.status.exceptionLevel) {
|
||||
if(code == 10) print("* reserved instruction @ ", hex(pipeline.address), " ", hex(pipeline.instruction), "\n");
|
||||
scc.epc = PC;
|
||||
scc.status.exceptionLevel = 1;
|
||||
scc.cause.exceptionCode = code;
|
||||
scc.cause.coprocessorError = coprocessor;
|
||||
if(scc.cause.branchDelay = branch.inDelaySlot()) scc.epc -= 4;
|
||||
} else {
|
||||
scc.cause.exceptionCode = code;
|
||||
scc.cause.coprocessorError = coprocessor;
|
||||
}
|
||||
|
||||
u64 vectorBase = !scc.status.vectorLocation ? i32(0x8000'0000) : i32(0xbfc0'0200);
|
||||
u32 vectorOffset = (code == 2 || code == 3) ? 0x0000 : 0x0180;
|
||||
PC = vectorBase + vectorOffset;
|
||||
branch.exception();
|
||||
context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::instruction() -> void {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = readWord(pipeline.address)(0);
|
||||
|
||||
if(auto interrupts = scc.cause.interruptPending & scc.status.interruptMask) {
|
||||
if(scc.status.interruptEnable && !scc.status.exceptionLevel && !scc.status.errorLevel) {
|
||||
if(debugger.tracer.interrupt->enabled()) {
|
||||
debugger.interrupt(hex(scc.cause.interruptPending, 2L));
|
||||
}
|
||||
scc.cause.interruptPending = interrupts;
|
||||
return raiseException(0, interrupts);
|
||||
}
|
||||
}
|
||||
|
||||
debugger.instruction();
|
||||
//instructionDEBUG();
|
||||
instructionEXECUTE();
|
||||
GPR[0].u64 = 0;
|
||||
|
||||
switch(branch.state) {
|
||||
case Branch::Step: PC += 4; break;
|
||||
case Branch::DelaySlot: PC = branch.pc; branch.reset(); break;
|
||||
case Branch::Take: PC += 4; branch.delaySlot(); break;
|
||||
case Branch::Exception: branch.reset(); break;
|
||||
case Branch::Discard: PC += 8; branch.reset(); break;
|
||||
}
|
||||
|
||||
if(--scc.random.index < scc.wired.index) {
|
||||
scc.random.index = 31;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDEBUG() -> void {
|
||||
static vector<bool> mask;
|
||||
if(!mask) mask.resize(0x08000000);
|
||||
//if(mask[(PC & 0x1fffffff) >> 2]) return;
|
||||
mask[(PC & 0x1fffffff) >> 2] = 1;
|
||||
|
||||
static uint counter = 0;
|
||||
//if(++counter > 100) return;
|
||||
print(
|
||||
disassembler.hint(hex(pipeline.address, 8L)), " ",
|
||||
//disassembler.hint(hex(pipeline.instruction, 8L)), " ",
|
||||
disassembler.disassemble(pipeline.address, pipeline.instruction), "\n"
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::instructionEXECUTE() -> void {
|
||||
switch(OP >> 26) {
|
||||
case 0x00: return instructionSPECIAL();
|
||||
case 0x01: return instructionREGIMM();
|
||||
case 0x02: return instructionJ();
|
||||
case 0x03: return instructionJAL();
|
||||
case 0x04: return instructionB(RS.u64 == RT.u64); //BEQ
|
||||
case 0x05: return instructionB(RS.u64 != RT.u64); //BNE
|
||||
case 0x06: return instructionB(RS.i64 <= 0); //BLEZ
|
||||
case 0x07: return instructionB(RS.i64 > 0); //BGTZ
|
||||
case 0x08: return instructionADDI();
|
||||
case 0x09: return instructionADDIU();
|
||||
case 0x0a: return instructionSLTI();
|
||||
case 0x0b: return instructionSLTIU();
|
||||
case 0x0c: return instructionANDI();
|
||||
case 0x0d: return instructionORI();
|
||||
case 0x0e: return instructionXORI();
|
||||
case 0x0f: return instructionLUI();
|
||||
case 0x10: return instructionCOP0();
|
||||
case 0x11: return instructionCOP1();
|
||||
case 0x12: return exception.coprocessor2(); //COP2
|
||||
case 0x13: return exception.coprocessor3(); //COP3
|
||||
case 0x14: return instructionBL(RS.u64 == RT.u64); //BEQL
|
||||
case 0x15: return instructionBL(RS.u64 != RT.u64); //BNEL
|
||||
case 0x16: return instructionBL(RS.i64 <= 0); //BLEZL
|
||||
case 0x17: return instructionBL(RS.i64 > 0); //BGTZL
|
||||
case 0x18: return instructionDADDI();
|
||||
case 0x19: return instructionDADDIU();
|
||||
case 0x1a: return instructionLDL();
|
||||
case 0x1b: return instructionLDR();
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return instructionLB();
|
||||
case 0x21: return instructionLH();
|
||||
case 0x22: return instructionLWL();
|
||||
case 0x23: return instructionLW();
|
||||
case 0x24: return instructionLBU();
|
||||
case 0x25: return instructionLHU();
|
||||
case 0x26: return instructionLWR();
|
||||
case 0x27: return instructionLWU();
|
||||
case 0x28: return instructionSB();
|
||||
case 0x29: return instructionSH();
|
||||
case 0x2a: return instructionSWL();
|
||||
case 0x2b: return instructionSW();
|
||||
case 0x2c: return instructionSDL();
|
||||
case 0x2d: return instructionSDR();
|
||||
case 0x2e: return instructionSWR();
|
||||
case 0x2f: return instructionCACHE();
|
||||
case 0x30: return instructionLL();
|
||||
case 0x31: return instructionLWC1();
|
||||
case 0x32: return exception.coprocessor2(); //LWC2
|
||||
case 0x33: return exception.coprocessor3(); //LWC3
|
||||
case 0x34: return instructionLLD();
|
||||
case 0x35: return instructionLDC1();
|
||||
case 0x36: return exception.coprocessor2(); //LDC2
|
||||
case 0x37: return instructionLD();
|
||||
case 0x38: return instructionSC();
|
||||
case 0x39: return instructionSWC1();
|
||||
case 0x3a: return exception.coprocessor2(); //SWC2
|
||||
case 0x3b: return exception.coprocessor3(); //SWC3
|
||||
case 0x3c: return instructionSCD();
|
||||
case 0x3d: return instructionSDC1();
|
||||
case 0x3e: return exception.coprocessor2(); //SDC2
|
||||
case 0x3f: return instructionSD();
|
||||
}
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionSPECIAL() -> void {
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionSLL();
|
||||
case 0x01: break;
|
||||
case 0x02: return instructionSRL();
|
||||
case 0x03: return instructionSRA();
|
||||
case 0x04: return instructionSLLV();
|
||||
case 0x05: break;
|
||||
case 0x06: return instructionSRLV();
|
||||
case 0x07: return instructionSRAV();
|
||||
case 0x08: return instructionJR();
|
||||
case 0x09: return instructionJALR();
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: return instructionSYSCALL();
|
||||
case 0x0d: return instructionBREAK();
|
||||
case 0x0e: break;
|
||||
case 0x0f: return; //SYNC
|
||||
case 0x10: return instructionMFHI();
|
||||
case 0x11: return instructionMTHI();
|
||||
case 0x12: return instructionMFLO();
|
||||
case 0x13: return instructionMTLO();
|
||||
case 0x14: return instructionDSLLV();
|
||||
case 0x15: break;
|
||||
case 0x16: return instructionDSRLV();
|
||||
case 0x17: return instructionDSRAV();
|
||||
case 0x18: return instructionMULT();
|
||||
case 0x19: return instructionMULTU();
|
||||
case 0x1a: return instructionDIV();
|
||||
case 0x1b: return instructionDIVU();
|
||||
case 0x1c: return instructionDMULT();
|
||||
case 0x1d: return instructionDMULTU();
|
||||
case 0x1e: return instructionDDIV();
|
||||
case 0x1f: return instructionDDIVU();
|
||||
case 0x20: return instructionADD();
|
||||
case 0x21: return instructionADDU();
|
||||
case 0x22: return instructionSUB();
|
||||
case 0x23: return instructionSUBU();
|
||||
case 0x24: return instructionAND();
|
||||
case 0x25: return instructionOR();
|
||||
case 0x26: return instructionXOR();
|
||||
case 0x27: return instructionNOR();
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return instructionSLT();
|
||||
case 0x2b: return instructionSLTU();
|
||||
case 0x2c: return instructionDADD();
|
||||
case 0x2d: return instructionDADDU();
|
||||
case 0x2e: return instructionDSUB();
|
||||
case 0x2f: return instructionDSUBU();
|
||||
case 0x30: return instructionTGE();
|
||||
case 0x31: return instructionTGEU();
|
||||
case 0x32: return instructionTLT();
|
||||
case 0x33: return instructionTLTU();
|
||||
case 0x34: return instructionTEQ();
|
||||
case 0x35: break;
|
||||
case 0x36: return instructionTNE();
|
||||
case 0x37: break;
|
||||
case 0x38: return instructionDSLL();
|
||||
case 0x39: break;
|
||||
case 0x3a: return instructionDSRL();
|
||||
case 0x3b: return instructionDSRA();
|
||||
case 0x3c: return instructionDSLL32();
|
||||
case 0x3d: break;
|
||||
case 0x3e: return instructionDSRL32();
|
||||
case 0x3f: return instructionDSRA32();
|
||||
}
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionREGIMM() -> void {
|
||||
switch(OP >> 16 & 0x1f) {
|
||||
case 0x00: return instructionB (RS.i64 < 0); //BLTZ
|
||||
case 0x01: return instructionB (RS.i64 >= 0); //BGEZ
|
||||
case 0x02: return instructionBL(RS.i64 < 0); //BLTZL
|
||||
case 0x03: return instructionBL(RS.i64 >= 0); //BGEZL
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: return instructionTGEI();
|
||||
case 0x09: return instructionTGEIU();
|
||||
case 0x0a: return instructionTLTI();
|
||||
case 0x0b: return instructionTLTIU();
|
||||
case 0x0c: return instructionTEQI();
|
||||
case 0x0d: break;
|
||||
case 0x0e: return instructionTNEI();
|
||||
case 0x0f: break;
|
||||
case 0x10: return instructionBAL (RS.i64 < 0); //BLTZAL
|
||||
case 0x11: return instructionBAL (RS.i64 >= 0); //BGEZAL
|
||||
case 0x12: return instructionBALL(RS.i64 < 0); //BLTZALL
|
||||
case 0x13: return instructionBALL(RS.i64 >= 0); //BGEZALL
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionCOP0() -> void {
|
||||
if(!scc.status.enable.coprocessor0) {
|
||||
if(context.mode != Context::Mode::Kernel) return exception.coprocessor0();
|
||||
}
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
case 0x00: return instructionMFC0();
|
||||
case 0x01: return instructionDMFC0();
|
||||
case 0x02: break; //CFC0
|
||||
case 0x04: return instructionMTC0();
|
||||
case 0x05: return instructionDMTC0();
|
||||
case 0x06: break; //CTC0
|
||||
case 0x08: break; //BC0F, BC0FL, BC0T, BC0TL
|
||||
}
|
||||
if(!(OP >> 25 & 1)) return exception.reservedInstruction();
|
||||
switch(OP & 0x3f) {
|
||||
case 0x01: return instructionTLBR();
|
||||
case 0x02: return instructionTLBWI();
|
||||
case 0x06: return instructionTLBWR();
|
||||
case 0x08: return instructionTLBP();
|
||||
case 0x18: return instructionERET();
|
||||
}
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
|
||||
auto CPU::instructionCOP1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
case 0x00: return instructionMFC1();
|
||||
case 0x01: return instructionDMFC1();
|
||||
case 0x02: return instructionCFC1();
|
||||
case 0x04: return instructionMTC1();
|
||||
case 0x05: return instructionDMTC1();
|
||||
case 0x06: return instructionCTC1();
|
||||
case 0x08: return instructionBC1();
|
||||
}
|
||||
if(!(OP >> 25 & 1)) return exception.reservedInstruction();
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionFADD();
|
||||
case 0x01: return instructionFSUB();
|
||||
case 0x02: return instructionFMUL();
|
||||
case 0x03: return instructionFDIV();
|
||||
case 0x04: return instructionFSQRT();
|
||||
case 0x05: return instructionFABS();
|
||||
case 0x06: return instructionFMOV();
|
||||
case 0x07: return instructionFNEG();
|
||||
case 0x08: return instructionFROUNDL();
|
||||
case 0x09: return instructionFTRUNCL();
|
||||
case 0x0a: return instructionFCEILL();
|
||||
case 0x0b: return instructionFFLOORL();
|
||||
case 0x0c: return instructionFROUNDW();
|
||||
case 0x0d: return instructionFTRUNCW();
|
||||
case 0x0e: return instructionFCEILW();
|
||||
case 0x0f: return instructionFFLOORW();
|
||||
case 0x20: return instructionFCVTS();
|
||||
case 0x21: return instructionFCVTD();
|
||||
case 0x24: return instructionFCVTW();
|
||||
case 0x25: return instructionFCVTL();
|
||||
case 0x30: case 0x38: return instructionFCF();
|
||||
case 0x31: case 0x39: return instructionFCUN();
|
||||
case 0x32: case 0x3a: return instructionFCEQ();
|
||||
case 0x33: case 0x3b: return instructionFCUEQ();
|
||||
case 0x34: case 0x3c: return instructionFCOLT();
|
||||
case 0x35: case 0x3d: return instructionFCULT();
|
||||
case 0x36: case 0x3e: return instructionFCOLE();
|
||||
case 0x37: case 0x3f: return instructionFCULE();
|
||||
}
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
auto CPU::readAddress(u32 address) -> maybe<u32> {
|
||||
switch(context.segment[address >> 29]) {
|
||||
case Context::Segment::Invalid:
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto lookup = tlb.load(address)) return lookup();
|
||||
tlb.exception(address);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Uncached:
|
||||
default:
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::writeAddress(u32 address) -> maybe<u32> {
|
||||
switch(context.segment[address >> 29]) {
|
||||
case Context::Segment::Invalid:
|
||||
exception.addressStore();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto lookup = tlb.store(address)) return lookup();
|
||||
tlb.exception(address);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Uncached:
|
||||
default:
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::readByte(u32 address) -> maybe<u32> {
|
||||
if(auto physical = readAddress(address)) return bus.readByte(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readHalf(u32 address) -> maybe<u32> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 1)) return exception.addressLoad(), nothing;
|
||||
}
|
||||
if(auto physical = readAddress(address)) return bus.readHalf(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readWord(u32 address) -> maybe<u32> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 3)) return exception.addressLoad(), nothing;
|
||||
}
|
||||
if(auto physical = readAddress(address)) return bus.readWord(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readDouble(u32 address) -> maybe<u64> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 7)) return exception.addressLoad(), nothing;
|
||||
}
|
||||
if(auto physical = readAddress(address)) return bus.readDouble(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::writeByte(u32 address, u8 data) -> bool {
|
||||
if(auto physical = writeAddress(address)) return bus.writeByte(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::writeHalf(u32 address, u16 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 1)) return exception.addressStore(), false;
|
||||
}
|
||||
if(auto physical = writeAddress(address)) return bus.writeHalf(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::writeWord(u32 address, u32 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 3)) return exception.addressStore(), false;
|
||||
}
|
||||
if(auto physical = writeAddress(address)) return bus.writeWord(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::writeDouble(u32 address, u64 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 7)) return exception.addressStore(), false;
|
||||
}
|
||||
if(auto physical = writeAddress(address)) return bus.writeDouble(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//{
|
||||
struct Pipeline {
|
||||
u32 address;
|
||||
u32 instruction;
|
||||
|
||||
struct InstructionCache {
|
||||
} ic;
|
||||
|
||||
struct RegisterFile {
|
||||
} rf;
|
||||
|
||||
struct Execution {
|
||||
} ex;
|
||||
|
||||
struct DataCache {
|
||||
} dc;
|
||||
|
||||
struct WriteBack {
|
||||
} wb;
|
||||
} pipeline;
|
||||
//};
|
||||
@@ -0,0 +1,47 @@
|
||||
#if !defined(CPU_REGISTERS_HPP)
|
||||
#define CPU_REGISTERS_HPP
|
||||
|
||||
//instruction decoding
|
||||
#define OP pipeline.instruction
|
||||
#define SA (OP >> 6 & 31)
|
||||
#define RDn (OP >> 11 & 31)
|
||||
#define RTn (OP >> 16 & 31)
|
||||
#define RSn (OP >> 21 & 31)
|
||||
#define IMMi16 i16(OP)
|
||||
#define IMMu16 u16(OP)
|
||||
#define IMMu26 (OP & 0x03ff'ffff)
|
||||
|
||||
//CPU
|
||||
#define GPR core.r
|
||||
#define LO core.lo
|
||||
#define HI core.hi
|
||||
#define PC core.pc
|
||||
|
||||
#define RD GPR[RDn]
|
||||
#define RT GPR[RTn]
|
||||
#define RS GPR[RSn]
|
||||
#define RA GPR[Core::Register::RA]
|
||||
#else
|
||||
#undef CPU_REGISTERS_HPP
|
||||
|
||||
//instruction decoding
|
||||
#undef OP
|
||||
#undef SA
|
||||
#undef RDn
|
||||
#undef RTn
|
||||
#undef RSn
|
||||
#undef IMMi16
|
||||
#undef IMMu16
|
||||
#undef IMMu26
|
||||
|
||||
//CPU
|
||||
#undef GPR
|
||||
#undef LO
|
||||
#undef HI
|
||||
#undef PC
|
||||
|
||||
#undef RD
|
||||
#undef RT
|
||||
#undef RS
|
||||
#undef RA
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
auto CPU::instructionDMFC0() -> void {
|
||||
RT.u64 = getControlRegister(RDn);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMTC0() -> void {
|
||||
setControlRegister(RDn, RT.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionERET() -> void {
|
||||
branch.exception();
|
||||
if(scc.status.errorLevel) {
|
||||
PC = scc.epcError;
|
||||
scc.status.errorLevel = 0;
|
||||
} else {
|
||||
PC = scc.epc;
|
||||
scc.status.exceptionLevel = 0;
|
||||
}
|
||||
scc.llbit = 0;
|
||||
context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::instructionMFC0() -> void {
|
||||
RT.u64 = i32(getControlRegister(RDn));
|
||||
}
|
||||
|
||||
auto CPU::instructionMTC0() -> void {
|
||||
setControlRegister(RDn, i32(RT.u32));
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBP() -> void {
|
||||
scc.index.tlbEntry = 0; //technically undefined
|
||||
scc.index.probeFailure = 1;
|
||||
for(uint index : range(TLB::Entries)) {
|
||||
auto& entry = tlb.entry[index];
|
||||
auto mask = ~entry.pageMask & ~0x1fff;
|
||||
if((entry.virtualAddress & mask) != (scc.tlb.virtualAddress & mask)) continue;
|
||||
if(!entry.global[0] || !entry.global[1]) {
|
||||
if(entry.addressSpaceID != scc.tlb.addressSpaceID) continue;
|
||||
}
|
||||
scc.index.tlbEntry = index;
|
||||
scc.index.probeFailure = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBR() -> void {
|
||||
if(scc.index.tlbEntry >= TLB::Entries) return;
|
||||
scc.tlb = tlb.entry[scc.index.tlbEntry];
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBWI() -> void {
|
||||
if(scc.index.tlbEntry >= TLB::Entries) return;
|
||||
tlb.entry[scc.index.tlbEntry] = scc.tlb;
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBWR() -> void {
|
||||
if(scc.random.index >= TLB::Entries) return;
|
||||
tlb.entry[scc.index.tlbEntry] = scc.tlb;
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
auto CPU::getControlRegister(uint5 index) -> u64 {
|
||||
uint64 data;
|
||||
switch(index) {
|
||||
case 0: //index
|
||||
data.bit( 0, 5) = scc.index.tlbEntry;
|
||||
data.bit(31) = scc.index.probeFailure;
|
||||
break;
|
||||
case 1: //random
|
||||
data.bit(0,4) = scc.random.index;
|
||||
data.bit(5) = scc.random.unused;
|
||||
break;
|
||||
case 2: //entrylo0
|
||||
data.bit(0) = scc.tlb.global[0];
|
||||
data.bit(1) = scc.tlb.valid[0];
|
||||
data.bit(2) = scc.tlb.dirty[0];
|
||||
data.bit(3, 5) = scc.tlb.cacheAlgorithm[0];
|
||||
data.bit(6,29) = scc.tlb.physicalAddress[0].bit(12,35);
|
||||
break;
|
||||
case 3: //entrylo1
|
||||
data.bit(0) = scc.tlb.global[1];
|
||||
data.bit(1) = scc.tlb.valid[1];
|
||||
data.bit(2) = scc.tlb.dirty[1];
|
||||
data.bit(3, 5) = scc.tlb.cacheAlgorithm[1];
|
||||
data.bit(6,29) = scc.tlb.physicalAddress[1].bit(12,35);
|
||||
break;
|
||||
case 4: //context
|
||||
data.bit( 4,22) = scc.context.badVirtualAddress;
|
||||
data.bit(23,63) = scc.context.pageTableEntryBase;
|
||||
break;
|
||||
case 5: //pagemask
|
||||
data.bit(13,24) = scc.tlb.pageMask.bit(13,24);
|
||||
break;
|
||||
case 6: //wired
|
||||
data.bit(0,4) = scc.wired.index;
|
||||
data.bit(5) = scc.wired.unused;
|
||||
break;
|
||||
case 8: //badvaddr
|
||||
data = scc.badVirtualAddress;
|
||||
break;
|
||||
case 9: //count
|
||||
data.bit(0,31) = scc.count;
|
||||
break;
|
||||
case 10: //entryhi
|
||||
data.bit( 0, 7) = scc.tlb.addressSpaceID;
|
||||
data.bit(13,39) = scc.tlb.virtualAddress.bit(13,31);
|
||||
data.bit(40,61) = scc.tlb.unused;
|
||||
data.bit(62,63) = scc.tlb.region;
|
||||
break;
|
||||
case 11: //compare
|
||||
data.bit(0,31) = scc.compare;
|
||||
break;
|
||||
case 12: //status
|
||||
data.bit( 0) = scc.status.interruptEnable;
|
||||
data.bit( 1) = scc.status.exceptionLevel;
|
||||
data.bit( 2) = scc.status.errorLevel;
|
||||
data.bit( 3, 4) = scc.status.privilegeMode;
|
||||
data.bit( 5) = scc.status.userExtendedAddressing;
|
||||
data.bit( 6) = scc.status.supervisorExtendedAddressing;
|
||||
data.bit( 7) = scc.status.kernelExtendedAddressing;
|
||||
data.bit( 8,15) = scc.status.interruptMask;
|
||||
data.bit(16) = scc.status.de;
|
||||
data.bit(17) = scc.status.ce;
|
||||
data.bit(18) = scc.status.condition;
|
||||
data.bit(20) = scc.status.softReset;
|
||||
data.bit(21) = scc.status.tlbShutdown;
|
||||
data.bit(22) = scc.status.vectorLocation;
|
||||
data.bit(24) = scc.status.instructionTracing;
|
||||
data.bit(25) = scc.status.reverseEndian;
|
||||
data.bit(26) = scc.status.floatingPointMode;
|
||||
data.bit(27) = scc.status.lowPowerMode;
|
||||
data.bit(28) = scc.status.enable.coprocessor0;
|
||||
data.bit(29) = scc.status.enable.coprocessor1;
|
||||
data.bit(30) = scc.status.enable.coprocessor2;
|
||||
data.bit(31) = scc.status.enable.coprocessor3;
|
||||
context.setMode();
|
||||
break;
|
||||
case 13: //cause
|
||||
data.bit( 2, 6) = scc.cause.exceptionCode;
|
||||
data.bit( 8,15) = scc.cause.interruptPending;
|
||||
data.bit(28,29) = scc.cause.coprocessorError;
|
||||
data.bit(31) = scc.cause.branchDelay;
|
||||
break;
|
||||
case 14: //exception program counter
|
||||
data = scc.epc;
|
||||
break;
|
||||
case 15: //coprocessor revision identifier
|
||||
data.bit(0, 7) = scc.coprocessor.revision;
|
||||
data.bit(8,15) = scc.coprocessor.implementation;
|
||||
break;
|
||||
case 16: //configuration
|
||||
data.bit( 0, 1) = scc.configuration.coherencyAlgorithmKSEG0;
|
||||
data.bit( 2, 3) = scc.configuration.cu;
|
||||
data.bit(15) = scc.configuration.bigEndian;
|
||||
data.bit(24,27) = scc.configuration.sysadWritebackPattern;
|
||||
data.bit(28,30) = scc.configuration.systemClockRatio;
|
||||
break;
|
||||
case 17: //load linked address
|
||||
data = scc.ll;
|
||||
break;
|
||||
case 18: //watchlo
|
||||
data.bit(0) = scc.watchLo.trapOnWrite;
|
||||
data.bit(1) = scc.watchLo.trapOnRead;
|
||||
data.bit(3,31) = scc.watchLo.physicalAddress.bit(3,31);
|
||||
break;
|
||||
case 19: //watchhi
|
||||
data.bit(0,3) = scc.watchHi.physicalAddressExtended;
|
||||
break;
|
||||
case 20: //xcontext
|
||||
data.bit( 4,30) = scc.xcontext.badVirtualAddress;
|
||||
data.bit(31,32) = scc.xcontext.region;
|
||||
data.bit(33,63) = scc.xcontext.pageTableEntryBase;
|
||||
break;
|
||||
case 26: //parity error
|
||||
data.bit(0,7) = scc.parityError.diagnostic;
|
||||
break;
|
||||
case 28: //taglo
|
||||
data.bit(6, 7) = scc.tagLo.primaryCacheState;
|
||||
data.bit(8,27) = scc.tagLo.physicalAddress.bit(12,31);
|
||||
break;
|
||||
case 31: //error exception program counter
|
||||
data = scc.epcError;
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::setControlRegister(uint5 index, uint64 data) -> void {
|
||||
//read-only variables are defined but commented out for documentation purposes
|
||||
switch(index) {
|
||||
case 0: //index
|
||||
scc.index.tlbEntry = data.bit( 0,5);
|
||||
scc.index.probeFailure = data.bit(31);
|
||||
break;
|
||||
case 1: //random
|
||||
//scc.random.index = data.bit(0,4);
|
||||
scc.random.unused = data.bit(5);
|
||||
break;
|
||||
case 2: //entrylo0
|
||||
scc.tlb.global[0] = data.bit(0);
|
||||
scc.tlb.valid[0] = data.bit(1);
|
||||
scc.tlb.dirty[0] = data.bit(2);
|
||||
scc.tlb.cacheAlgorithm[0] = data.bit(3, 5);
|
||||
scc.tlb.physicalAddress[0].bit(12,35) = data.bit(6,29);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 3: //entrylo1
|
||||
scc.tlb.global[1] = data.bit(0);
|
||||
scc.tlb.valid[1] = data.bit(1);
|
||||
scc.tlb.dirty[1] = data.bit(2);
|
||||
scc.tlb.cacheAlgorithm[1] = data.bit(3, 5);
|
||||
scc.tlb.physicalAddress[1].bit(12,35) = data.bit(6,29);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 4: //context
|
||||
scc.context.badVirtualAddress = data.bit( 4,22);
|
||||
scc.context.pageTableEntryBase = data.bit(23,63);
|
||||
break;
|
||||
case 5: //pagemask
|
||||
scc.tlb.pageMask.bit(13,24) = data.bit(13,24);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 6: //wired
|
||||
scc.wired.index = data.bit(0,4);
|
||||
scc.wired.unused = data.bit(5);
|
||||
scc.random.index = 31;
|
||||
break;
|
||||
case 8: //badvaddr
|
||||
//scc.badVirtualAddress = data; //read-only
|
||||
break;
|
||||
case 9: //count
|
||||
scc.count = data.bit(0,31);
|
||||
break;
|
||||
case 10: //entryhi
|
||||
scc.tlb.addressSpaceID = data.bit( 0, 7);
|
||||
scc.tlb.virtualAddress.bit(13,31) = data.bit(13,39);
|
||||
scc.tlb.unused = data.bit(40,61);
|
||||
scc.tlb.region = data.bit(62,63);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 11: //compare
|
||||
scc.compare = data.bit(0,31);
|
||||
scc.cause.interruptPending.bit(Interrupt::Timer) = 0;
|
||||
break;
|
||||
case 12: //status
|
||||
scc.status.interruptEnable = data.bit( 0);
|
||||
scc.status.exceptionLevel = data.bit( 1);
|
||||
scc.status.errorLevel = data.bit( 2);
|
||||
scc.status.privilegeMode = data.bit( 3, 4);
|
||||
scc.status.userExtendedAddressing = data.bit( 5);
|
||||
scc.status.supervisorExtendedAddressing = data.bit( 6);
|
||||
scc.status.kernelExtendedAddressing = data.bit( 7);
|
||||
scc.status.interruptMask = data.bit( 8,15);
|
||||
scc.status.de = data.bit(16);
|
||||
scc.status.ce = data.bit(17);
|
||||
scc.status.condition = data.bit(18);
|
||||
scc.status.softReset = data.bit(20);
|
||||
//scc.status.tlbShutdown = data.bit(21); //read-only
|
||||
scc.status.vectorLocation = data.bit(22);
|
||||
scc.status.instructionTracing = data.bit(24);
|
||||
scc.status.reverseEndian = data.bit(25);
|
||||
scc.status.floatingPointMode = data.bit(26);
|
||||
scc.status.lowPowerMode = data.bit(27);
|
||||
scc.status.enable.coprocessor0 = data.bit(28);
|
||||
scc.status.enable.coprocessor1 = data.bit(29);
|
||||
scc.status.enable.coprocessor2 = data.bit(30);
|
||||
scc.status.enable.coprocessor3 = data.bit(31);
|
||||
break;
|
||||
case 13: //cause
|
||||
scc.cause.interruptPending.bit(0) = data.bit(8);
|
||||
scc.cause.interruptPending.bit(1) = data.bit(9);
|
||||
break;
|
||||
case 14: //exception program counter
|
||||
scc.epc = data;
|
||||
break;
|
||||
case 15: //coprocessor revision identifier
|
||||
//scc.coprocessor.revision = data.bit(0, 7); //read-only
|
||||
//scc.coprocessor.implementation = data.bit(8,15); //read-only
|
||||
break;
|
||||
case 16: //configuration
|
||||
scc.configuration.coherencyAlgorithmKSEG0 = data.bit( 0, 1);
|
||||
scc.configuration.cu = data.bit( 2, 3);
|
||||
scc.configuration.bigEndian = data.bit(15);
|
||||
scc.configuration.sysadWritebackPattern = data.bit(24,27);
|
||||
//scc.configuration.systemClockRatio = data.bit(28,30); //read-only
|
||||
break;
|
||||
case 17: //load linked address
|
||||
scc.ll = data;
|
||||
break;
|
||||
case 18: //watchlo
|
||||
scc.watchLo.trapOnWrite = data.bit(0);
|
||||
scc.watchLo.trapOnRead = data.bit(1);
|
||||
scc.watchLo.physicalAddress.bit(3,31) = data.bit(3,31);
|
||||
break;
|
||||
case 19: //watchhi
|
||||
scc.watchHi.physicalAddressExtended = data.bit(0,3);
|
||||
break;
|
||||
case 20: //xcontext
|
||||
scc.xcontext.badVirtualAddress = data.bit( 4,30);
|
||||
scc.xcontext.region = data.bit(31,32);
|
||||
scc.xcontext.pageTableEntryBase = data.bit(33,63);
|
||||
break;
|
||||
case 26: //parity error
|
||||
scc.parityError.diagnostic = data.bit(0,7);
|
||||
break;
|
||||
case 28: //taglo
|
||||
scc.tagLo.primaryCacheState = data.bit(6, 7);
|
||||
scc.tagLo.physicalAddress.bit(12,31) = data.bit(8,27);
|
||||
break;
|
||||
case 31: //error exception program counter
|
||||
scc.epcError = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
auto CPU::TLB::load(u32 address) -> maybe<u32> {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals) continue;
|
||||
if((address & entry.addressMaskHi) != entry.addressCompare) continue;
|
||||
bool lo = address & entry.addressSelect;
|
||||
if(!entry.valid[lo]) return self.exception.tlbLoad(), nothing;
|
||||
return physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
|
||||
}
|
||||
return self.exception.tlbLoad(), nothing;
|
||||
}
|
||||
|
||||
auto CPU::TLB::store(u32 address) -> maybe<u32> {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals) continue;
|
||||
if((address & entry.addressMaskHi) != entry.addressCompare) continue;
|
||||
bool lo = address & entry.addressSelect;
|
||||
if(!entry.valid[lo]) return self.exception.tlbStore(), nothing;
|
||||
if(!entry.dirty[lo]) return self.exception.tlbModification(), nothing;
|
||||
return physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
|
||||
}
|
||||
return self.exception.tlbStore(), nothing;
|
||||
}
|
||||
|
||||
auto CPU::TLB::exception(u32 address) -> void {
|
||||
self.scc.badVirtualAddress = address;
|
||||
self.scc.tlb.addressSpaceID = address >> 32; //note: needs u64 address
|
||||
self.scc.tlb.virtualAddress = address >> 13;
|
||||
self.scc.context.badVirtualAddress = address >> 13;
|
||||
self.scc.xcontext.badVirtualAddress = address >> 13;
|
||||
}
|
||||
|
||||
auto CPU::TLB::Entry::synchronize() -> void {
|
||||
globals = global[0] && global[1];
|
||||
addressMaskHi = ~(pageMask | 0x1fff);
|
||||
addressMaskLo = (pageMask | 0x1fff) >> 1;
|
||||
addressSelect = addressMaskLo + 1;
|
||||
addressCompare = virtualAddress & addressMaskHi;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//{
|
||||
//scc-registers.cpp
|
||||
auto getControlRegister(uint5) -> u64;
|
||||
auto setControlRegister(uint5, uint64) -> void;
|
||||
|
||||
//Translation Lookaside Buffer
|
||||
struct TLB {
|
||||
CPU& self;
|
||||
TLB(CPU& self) : self(self) {}
|
||||
static constexpr uint Entries = 32;
|
||||
|
||||
//tlb.cpp
|
||||
auto load(u32 address) -> maybe<u32>;
|
||||
auto store(u32 address) -> maybe<u32>;
|
||||
auto exception(u32 address) -> void;
|
||||
|
||||
struct Entry {
|
||||
//scc-tlb.cpp
|
||||
auto synchronize() -> void;
|
||||
|
||||
uint1 global[2] = {};
|
||||
uint1 valid[2] = {};
|
||||
uint1 dirty[2] = {};
|
||||
uint3 cacheAlgorithm[2] = {2, 2};
|
||||
uint32 physicalAddress[2] = {};
|
||||
uint32 pageMask = 0;
|
||||
uint32 virtualAddress = 0;
|
||||
uint8 addressSpaceID = 0;
|
||||
//unimplemented:
|
||||
uint22 unused = 0;
|
||||
uint2 region = 0;
|
||||
//internal:
|
||||
uint1 globals = 0;
|
||||
uint32 addressMaskHi = 0;
|
||||
uint32 addressMaskLo = 0;
|
||||
uint32 addressSelect = 0;
|
||||
uint32 addressCompare = 0;
|
||||
} entry[TLB::Entries];
|
||||
|
||||
u32 physicalAddress = 0;
|
||||
} tlb{*this};
|
||||
|
||||
//System Control Coprocessor
|
||||
struct SCC {
|
||||
//0
|
||||
struct Index {
|
||||
uint6 tlbEntry = 0;
|
||||
uint1 probeFailure = 0;
|
||||
} index;
|
||||
|
||||
//1
|
||||
struct Random {
|
||||
uint5 index = 31;
|
||||
uint1 unused = 0;
|
||||
} random;
|
||||
|
||||
//2: EntryLo0
|
||||
//3: EntryLo1
|
||||
//5: PageMask
|
||||
//10: EntryHi
|
||||
TLB::Entry tlb;
|
||||
|
||||
//4
|
||||
struct Context {
|
||||
uint19 badVirtualAddress = 0;
|
||||
uint41 pageTableEntryBase = 0;
|
||||
} context;
|
||||
|
||||
//6
|
||||
struct Wired {
|
||||
uint5 index = 0;
|
||||
uint1 unused = 0;
|
||||
} wired;
|
||||
|
||||
//8
|
||||
u64 badVirtualAddress = 0;
|
||||
|
||||
//9
|
||||
u32 count = 0;
|
||||
|
||||
//11
|
||||
u32 compare = 0;
|
||||
|
||||
//12
|
||||
struct Status {
|
||||
uint1 interruptEnable = 0;
|
||||
uint1 exceptionLevel = 0;
|
||||
uint1 errorLevel = 1;
|
||||
uint2 privilegeMode = 0;
|
||||
uint1 userExtendedAddressing = 0;
|
||||
uint1 supervisorExtendedAddressing = 0;
|
||||
uint1 kernelExtendedAddressing = 0;
|
||||
uint8 interruptMask = 0xff;
|
||||
uint1 de = 0; //unused
|
||||
uint1 ce = 0; //unused
|
||||
uint1 condition = 0;
|
||||
uint1 softReset = 0;
|
||||
uint1 tlbShutdown = 0;
|
||||
uint1 vectorLocation = 1;
|
||||
uint1 instructionTracing = 0;
|
||||
uint1 reverseEndian = 0;
|
||||
uint1 floatingPointMode = 1;
|
||||
uint1 lowPowerMode = 0;
|
||||
struct Enable {
|
||||
uint1 coprocessor0 = 1;
|
||||
uint1 coprocessor1 = 1;
|
||||
uint1 coprocessor2 = 0;
|
||||
uint1 coprocessor3 = 0;
|
||||
} enable;
|
||||
} status;
|
||||
|
||||
//13
|
||||
struct Cause {
|
||||
uint5 exceptionCode = 0;
|
||||
uint8 interruptPending = 0x00;
|
||||
uint2 coprocessorError = 0;
|
||||
uint1 branchDelay = 0;
|
||||
} cause;
|
||||
|
||||
//14: Exception Program Counter
|
||||
u64 epc;
|
||||
|
||||
//15: Coprocessor Revision Identifier
|
||||
struct Coprocessor {
|
||||
static constexpr u8 revision = 0x00;
|
||||
static constexpr u8 implementation = 0x0b;
|
||||
} coprocessor;
|
||||
|
||||
//16
|
||||
struct Configuration {
|
||||
uint2 coherencyAlgorithmKSEG0 = 0;
|
||||
uint2 cu = 0; //reserved
|
||||
uint1 bigEndian = 1;
|
||||
uint2 sysadWritebackPattern = 0;
|
||||
uint2 systemClockRatio = 6;
|
||||
} configuration;
|
||||
|
||||
//17: Load Linked Address
|
||||
u64 ll;
|
||||
uint1 llbit;
|
||||
|
||||
//18
|
||||
struct WatchLo {
|
||||
uint1 trapOnWrite = 0;
|
||||
uint1 trapOnRead = 0;
|
||||
uint32 physicalAddress = 0;
|
||||
} watchLo;
|
||||
|
||||
//19
|
||||
struct WatchHi {
|
||||
uint4 physicalAddressExtended = 0; //unused; for R4000 compatibility only
|
||||
} watchHi;
|
||||
|
||||
//20
|
||||
struct XContext {
|
||||
uint27 badVirtualAddress = 0;
|
||||
uint2 region = 0;
|
||||
uint31 pageTableEntryBase = 0;
|
||||
} xcontext;
|
||||
|
||||
//26
|
||||
struct ParityError {
|
||||
uint8 diagnostic = 0; //unused; for R4000 compatibility only
|
||||
} parityError;
|
||||
|
||||
//28
|
||||
struct TagLo {
|
||||
uint2 primaryCacheState = 0;
|
||||
uint32 physicalAddress = 0;
|
||||
} tagLo;
|
||||
|
||||
//31: Error Exception Program Counter
|
||||
u64 epcError;
|
||||
} scc;
|
||||
|
||||
//scc-instructions.cpp
|
||||
auto instructionDMFC0() -> void;
|
||||
auto instructionDMTC0() -> void;
|
||||
auto instructionERET() -> void;
|
||||
auto instructionMFC0() -> void;
|
||||
auto instructionMTC0() -> void;
|
||||
auto instructionTLBP() -> void;
|
||||
auto instructionTLBR() -> void;
|
||||
auto instructionTLBWI() -> void;
|
||||
auto instructionTLBWR() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,2 @@
|
||||
auto CPU::serializeR4300(serializer& s) -> void {
|
||||
}
|
||||
+23
-6
@@ -3,28 +3,45 @@
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
CPU cpu;
|
||||
#include "core/core.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("CPU");
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto CPU::unload() -> void {
|
||||
node = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
step(20);
|
||||
instruction();
|
||||
step(2);
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
vi.clock -= clocks;
|
||||
ai.clock -= clocks;
|
||||
rsp.clock -= clocks;
|
||||
rdp.clock -= clocks;
|
||||
while( vi.clock < 0) vi.main();
|
||||
while( ai.clock < 0) ai.main();
|
||||
while(rsp.clock < 0) rsp.main();
|
||||
while(rdp.clock < 0) rdp.main();
|
||||
|
||||
while(clocks--) {
|
||||
if(++scc.count == scc.compare) {
|
||||
scc.cause.interruptPending.bit(Interrupt::Timer) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
VR4300::power();
|
||||
Thread::create(93'750'000, {&CPU::main, this});
|
||||
auto CPU::power(bool reset) -> void {
|
||||
Thread::reset();
|
||||
powerR4300(reset);
|
||||
}
|
||||
|
||||
}
|
||||
+18
-2
@@ -1,8 +1,22 @@
|
||||
//NEC VR4300
|
||||
|
||||
struct CPU : VR4300, Thread {
|
||||
struct CPU : Thread {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto instruction() -> void;
|
||||
auto exception(string_view) -> void;
|
||||
auto interrupt(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Instruction instruction;
|
||||
Node::Notification exception;
|
||||
Node::Notification interrupt;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//cpu.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
@@ -10,10 +24,12 @@ struct CPU : VR4300, Thread {
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
#include "core/core.hpp"
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
@@ -0,0 +1,31 @@
|
||||
auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Instruction>("Instruction", "CPU");
|
||||
tracer.instruction->setAddressBits(32);
|
||||
|
||||
tracer.exception = parent->append<Node::Notification>("Exception", "CPU");
|
||||
tracer.interrupt = parent->append<Node::Notification>("Interrupt", "CPU");
|
||||
}
|
||||
|
||||
auto CPU::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled()) {
|
||||
u32 address = cpu.pipeline.address;
|
||||
u32 instruction = cpu.pipeline.instruction;
|
||||
if(tracer.instruction->address(address)) {
|
||||
cpu.disassembler.showColors = 0;
|
||||
tracer.instruction->notify(cpu.disassembler.disassemble(address, instruction), {});
|
||||
cpu.disassembler.showColors = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Debugger::exception(string_view type) -> void {
|
||||
if(tracer.exception->enabled()) {
|
||||
tracer.exception->notify(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Debugger::interrupt(string_view type) -> void {
|
||||
if(tracer.interrupt->enabled()) {
|
||||
tracer.interrupt->notify(type);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
auto CPU::serialize(serializer& s) -> void {
|
||||
VR4300::serialize(s);
|
||||
Thread::serialize(s);
|
||||
serializeR4300(s);
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
inline auto Bus::readByte(u32 address) -> u8 {
|
||||
static auto transform = [&](u32 data) -> u8 {
|
||||
switch(address & 3) {
|
||||
case 0: return data >> 24;
|
||||
case 1: return data >> 16;
|
||||
case 2: return data >> 8;
|
||||
case 3: return data >> 0;
|
||||
} unreachable;
|
||||
};
|
||||
|
||||
address &= 0x1fff'ffff;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.readByte(address);
|
||||
if(address <= 0x03ef'ffff) return 0;
|
||||
if(address <= 0x03ff'ffff) return transform(rdram.readIO(address));
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.readByte(address);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.readByte(address);
|
||||
if(address <= 0x0403'ffff) return 0;
|
||||
if(address <= 0x0407'ffff) return transform(rsp.readSCC(address));
|
||||
if(address <= 0x040f'ffff) return transform(rsp.readIO(address));
|
||||
if(address <= 0x041f'ffff) return transform(rdp.readSCC(address));
|
||||
if(address <= 0x042f'ffff) return transform(rdp.readIO(address));
|
||||
if(address <= 0x043f'ffff) return transform(mi.readIO(address));
|
||||
if(address <= 0x044f'ffff) return transform(vi.readIO(address));
|
||||
if(address <= 0x045f'ffff) return transform(ai.readIO(address));
|
||||
if(address <= 0x046f'ffff) return transform(pi.readIO(address));
|
||||
if(address <= 0x047f'ffff) return transform(ri.readIO(address));
|
||||
if(address <= 0x048f'ffff) return transform(si.readIO(address));
|
||||
if(address <= 0x0fff'ffff) return 0;
|
||||
if(address <= 0x1fbf'ffff) return cartridge.rom.readByte(address);
|
||||
if(address <= 0x1fc0'07bf) return pi.rom.readByte(address);
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.readByte(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline auto Bus::readHalf(u32 address) -> u16 {
|
||||
static auto transform = [&](u32 data) -> u16 {
|
||||
switch(address & 2) {
|
||||
case 0: return data >> 16;
|
||||
case 2: return data >> 0;
|
||||
} unreachable;
|
||||
};
|
||||
|
||||
address &= 0x1fff'fffe;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.readHalf(address);
|
||||
if(address <= 0x03ef'ffff) return 0;
|
||||
if(address <= 0x03ff'ffff) return transform(rdram.readIO(address));
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.readHalf(address);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.readHalf(address);
|
||||
if(address <= 0x0403'ffff) return 0;
|
||||
if(address <= 0x0407'ffff) return transform(rsp.readSCC(address));
|
||||
if(address <= 0x040f'ffff) return transform(rsp.readIO(address));
|
||||
if(address <= 0x041f'ffff) return transform(rdp.readSCC(address));
|
||||
if(address <= 0x042f'ffff) return transform(rdp.readIO(address));
|
||||
if(address <= 0x043f'ffff) return transform(mi.readIO(address));
|
||||
if(address <= 0x044f'ffff) return transform(vi.readIO(address));
|
||||
if(address <= 0x045f'ffff) return transform(ai.readIO(address));
|
||||
if(address <= 0x046f'ffff) return transform(pi.readIO(address));
|
||||
if(address <= 0x047f'ffff) return transform(ri.readIO(address));
|
||||
if(address <= 0x048f'ffff) return transform(si.readIO(address));
|
||||
if(address <= 0x0fff'ffff) return 0;
|
||||
if(address <= 0x1fbf'ffff) return cartridge.rom.readHalf(address);
|
||||
if(address <= 0x1fc0'07bf) return pi.rom.readHalf(address);
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.readHalf(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline auto Bus::readWord(u32 address) -> u32 {
|
||||
address &= 0x1fff'fffc;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.readWord(address);
|
||||
if(address <= 0x03ef'ffff) return 0;
|
||||
if(address <= 0x03ff'ffff) return rdram.readIO(address);
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.readWord(address);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.readWord(address);
|
||||
if(address <= 0x0403'ffff) return 0;
|
||||
if(address <= 0x0407'ffff) return rsp.readSCC(address);
|
||||
if(address <= 0x040f'ffff) return rsp.readIO(address);
|
||||
if(address <= 0x041f'ffff) return rdp.readSCC(address);
|
||||
if(address <= 0x042f'ffff) return rdp.readIO(address);
|
||||
if(address <= 0x043f'ffff) return mi.readIO(address);
|
||||
if(address <= 0x044f'ffff) return vi.readIO(address);
|
||||
if(address <= 0x045f'ffff) return ai.readIO(address);
|
||||
if(address <= 0x046f'ffff) return pi.readIO(address);
|
||||
if(address <= 0x047f'ffff) return ri.readIO(address);
|
||||
if(address <= 0x048f'ffff) return si.readIO(address);
|
||||
if(address <= 0x0fff'ffff) return 0;
|
||||
if(address <= 0x1fbf'ffff) return cartridge.rom.readWord(address);
|
||||
if(address <= 0x1fc0'07bf) return pi.rom.readWord(address);
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.readWord(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline auto Bus::readDouble(u32 address) -> u64 {
|
||||
address &= 0x1fff'fff8;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.readDouble(address);
|
||||
if(address <= 0x03ef'ffff) return 0;
|
||||
if(address <= 0x03ff'ffff) { u64 data = rdram.readIO(address); return data << 32 | rdram.readIO(address + 4); }
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.readDouble(address);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.readDouble(address);
|
||||
if(address <= 0x0403'ffff) return 0;
|
||||
if(address <= 0x0407'ffff) { u64 data = rsp.readSCC(address); return data << 32 | rsp.readSCC (address + 4); }
|
||||
if(address <= 0x040f'ffff) { u64 data = rsp.readIO(address); return data << 32 | rsp.readIO (address + 4); }
|
||||
if(address <= 0x041f'ffff) { u64 data = rdp.readSCC(address); return data << 32 | rdp.readSCC (address + 4); }
|
||||
if(address <= 0x042f'ffff) { u64 data = rdp.readIO(address); return data << 32 | rdp.readIO (address + 4); }
|
||||
if(address <= 0x043f'ffff) { u64 data = mi.readIO(address); return data << 32 | mi.readIO (address + 4); }
|
||||
if(address <= 0x044f'ffff) { u64 data = vi.readIO(address); return data << 32 | vi.readIO (address + 4); }
|
||||
if(address <= 0x045f'ffff) { u64 data = ai.readIO(address); return data << 32 | ai.readIO (address + 4); }
|
||||
if(address <= 0x046f'ffff) { u64 data = pi.readIO(address); return data << 32 | pi.readIO (address + 4); }
|
||||
if(address <= 0x047f'ffff) { u64 data = ri.readIO(address); return data << 32 | ri.readIO (address + 4); }
|
||||
if(address <= 0x048f'ffff) { u64 data = si.readIO(address); return data << 32 | si.readIO (address + 4); }
|
||||
if(address <= 0x0fff'ffff) return 0;
|
||||
if(address <= 0x1fbf'ffff) return cartridge.rom.readDouble(address);
|
||||
if(address <= 0x1fc0'07bf) return pi.rom.readDouble(address);
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.readDouble(address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline auto Bus::writeByte(u32 address, u8 data) -> void {
|
||||
static auto transform = [&](u32 data) -> u32 {
|
||||
switch(address & 3) {
|
||||
case 0: return data << 24;
|
||||
case 1: return data << 16;
|
||||
case 2: return data << 8;
|
||||
case 3: return data << 0;
|
||||
} unreachable;
|
||||
};
|
||||
|
||||
address &= 0x1fff'ffff;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.writeByte(address, data);
|
||||
if(address <= 0x03ef'ffff) return;
|
||||
if(address <= 0x03ff'ffff) return rdram.writeIO(address, transform(data));
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.writeByte(address, data);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.writeByte(address, data);
|
||||
if(address <= 0x0403'ffff) return;
|
||||
if(address <= 0x0407'ffff) return rsp.writeSCC(address, transform(data));
|
||||
if(address <= 0x040f'ffff) return rsp.writeIO(address, transform(data));
|
||||
if(address <= 0x041f'ffff) return rdp.writeSCC(address, transform(data));
|
||||
if(address <= 0x042f'ffff) return rdp.writeIO(address, transform(data));
|
||||
if(address <= 0x043f'ffff) return mi.writeIO(address, transform(data));
|
||||
if(address <= 0x044f'ffff) return vi.writeIO(address, transform(data));
|
||||
if(address <= 0x045f'ffff) return ai.writeIO(address, transform(data));
|
||||
if(address <= 0x046f'ffff) return pi.writeIO(address, transform(data));
|
||||
if(address <= 0x047f'ffff) return ri.writeIO(address, transform(data));
|
||||
if(address <= 0x048f'ffff) return si.writeIO(address, transform(data));
|
||||
if(address <= 0x0fff'ffff) return;
|
||||
if(address <= 0x1fbf'ffff) return;
|
||||
if(address <= 0x1fc0'07bf) return;
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.writeByte(address, data);
|
||||
return;
|
||||
}
|
||||
|
||||
inline auto Bus::writeHalf(u32 address, u16 data) -> void {
|
||||
static auto transform = [&](u32 data) -> u32 {
|
||||
switch(address & 2) {
|
||||
case 0: return data << 16;
|
||||
case 2: return data << 0;
|
||||
} unreachable;
|
||||
};
|
||||
|
||||
address &= 0x1fff'fffe;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.writeHalf(address, data);
|
||||
if(address <= 0x03ef'ffff) return;
|
||||
if(address <= 0x03ff'ffff) return rdram.writeIO(address, transform(data));
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.writeHalf(address, data);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.writeHalf(address, data);
|
||||
if(address <= 0x0403'ffff) return;
|
||||
if(address <= 0x0407'ffff) return rsp.writeSCC(address, transform(data));
|
||||
if(address <= 0x040f'ffff) return rsp.writeIO(address, transform(data));
|
||||
if(address <= 0x041f'ffff) return rdp.writeSCC(address, transform(data));
|
||||
if(address <= 0x042f'ffff) return rdp.writeIO(address, transform(data));
|
||||
if(address <= 0x043f'ffff) return mi.writeIO(address, transform(data));
|
||||
if(address <= 0x044f'ffff) return vi.writeIO(address, transform(data));
|
||||
if(address <= 0x045f'ffff) return ai.writeIO(address, transform(data));
|
||||
if(address <= 0x046f'ffff) return pi.writeIO(address, transform(data));
|
||||
if(address <= 0x047f'ffff) return ri.writeIO(address, transform(data));
|
||||
if(address <= 0x048f'ffff) return si.writeIO(address, transform(data));
|
||||
if(address <= 0x0fff'ffff) return;
|
||||
if(address <= 0x1fbf'ffff) return;
|
||||
if(address <= 0x1fc0'07bf) return;
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.writeHalf(address, data);
|
||||
return;
|
||||
}
|
||||
|
||||
inline auto Bus::writeWord(u32 address, u32 data) -> void {
|
||||
address &= 0x1fff'fffc;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.writeWord(address, data);
|
||||
if(address <= 0x03ef'ffff) return;
|
||||
if(address <= 0x03ff'ffff) return rdram.writeIO(address, data);
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.writeWord(address, data);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.writeWord(address, data);
|
||||
if(address <= 0x0403'ffff) return;
|
||||
if(address <= 0x0407'ffff) return rsp.writeSCC(address, data);
|
||||
if(address <= 0x040f'ffff) return rsp.writeIO(address, data);
|
||||
if(address <= 0x041f'ffff) return rdp.writeSCC(address, data);
|
||||
if(address <= 0x042f'ffff) return rdp.writeIO(address, data);
|
||||
if(address <= 0x043f'ffff) return mi.writeIO(address, data);
|
||||
if(address <= 0x044f'ffff) return vi.writeIO(address, data);
|
||||
if(address <= 0x045f'ffff) return ai.writeIO(address, data);
|
||||
if(address <= 0x046f'ffff) return pi.writeIO(address, data);
|
||||
if(address <= 0x047f'ffff) return ri.writeIO(address, data);
|
||||
if(address <= 0x048f'ffff) return si.writeIO(address, data);
|
||||
if(address <= 0x0fff'ffff) return;
|
||||
if(address <= 0x1fbf'ffff) return;
|
||||
if(address <= 0x1fc0'07bf) return;
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.writeWord(address, data);
|
||||
return;
|
||||
}
|
||||
|
||||
inline auto Bus::writeDouble(u32 address, u64 data) -> void {
|
||||
address &= 0x1fff'fff8;
|
||||
if(address <= 0x007f'ffff) return rdram.ram.writeDouble(address, data);
|
||||
if(address <= 0x03ef'ffff) return;
|
||||
if(address <= 0x03ff'ffff) { rdram.writeIO(address, data >> 32); return rdram.writeIO(address + 4, data); }
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.writeDouble(address, data);
|
||||
if(address <= 0x0400'1fff) return rsp.imem.writeDouble(address, data);
|
||||
if(address <= 0x0403'ffff) return;
|
||||
if(address <= 0x0407'ffff) { rsp.writeSCC (address, data >> 32); return rsp.writeSCC (address + 4, data); }
|
||||
if(address <= 0x040f'ffff) { rsp.writeIO (address, data >> 32); return rsp.writeIO (address + 4, data); }
|
||||
if(address <= 0x041f'ffff) { rdp.writeSCC (address, data >> 32); return rdp.writeSCC (address + 4, data); }
|
||||
if(address <= 0x042f'ffff) { rdp.writeIO (address, data >> 32); return rdp.writeIO (address + 4, data); }
|
||||
if(address <= 0x043f'ffff) { mi.writeIO (address, data >> 32); return mi.writeIO (address + 4, data); }
|
||||
if(address <= 0x044f'ffff) { vi.writeIO (address, data >> 32); return vi.writeIO (address + 4, data); }
|
||||
if(address <= 0x045f'ffff) { ai.writeIO (address, data >> 32); return ai.writeIO (address + 4, data); }
|
||||
if(address <= 0x046f'ffff) { pi.writeIO (address, data >> 32); return pi.writeIO (address + 4, data); }
|
||||
if(address <= 0x047f'ffff) { ri.writeIO (address, data >> 32); return ri.writeIO (address + 4, data); }
|
||||
if(address <= 0x048f'ffff) { si.writeIO (address, data >> 32); return si.writeIO (address + 4, data); }
|
||||
if(address <= 0x0fff'ffff) return;
|
||||
if(address <= 0x1fbf'ffff) return;
|
||||
if(address <= 0x1fc0'07bf) return;
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.writeDouble(address, data);
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
Bus bus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//the N64 is configured for big-endian mode.
|
||||
//given the most common memory accesses are words,
|
||||
//optimize accesses by special-casing bytes anf halves instead.
|
||||
struct Memory {
|
||||
auto bytes() const -> u32 { return size >> 0; }
|
||||
auto halves() const -> u32 { return size >> 1; }
|
||||
auto words() const -> u32 { return size >> 2; }
|
||||
auto doubles() const -> u32 { return size >> 3; }
|
||||
auto quads() const -> u32 { return size >> 4; }
|
||||
|
||||
auto reset() -> void {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
maskByte = 0;
|
||||
maskHalf = 0;
|
||||
maskWord = 0;
|
||||
maskDouble = 0;
|
||||
maskQuad = 0;
|
||||
}
|
||||
|
||||
auto allocate(u32 capacity) -> void {
|
||||
size = capacity & ~7;
|
||||
u32 mask = bit::round(size) - 1;
|
||||
maskByte = mask & ~0;
|
||||
maskHalf = mask & ~1;
|
||||
maskWord = mask & ~3;
|
||||
maskDouble = mask & ~7;
|
||||
maskQuad = mask & ~15;
|
||||
data = new u8[mask + 1];
|
||||
}
|
||||
|
||||
auto fill(u32 value = 0) -> void {
|
||||
for(uint address = 0; address < size; address += 4) {
|
||||
writeWord(address, value);
|
||||
}
|
||||
}
|
||||
|
||||
auto load(Shared::File fp) -> void {
|
||||
for(uint address = 0; address < min(size, fp->size()); address += 4) {
|
||||
writeWord(address, fp->readm(4));
|
||||
}
|
||||
}
|
||||
|
||||
//N64 CPU requires aligned memory accesses
|
||||
auto readByte(u32 address) -> u8 { return *(u8*)&data[address & maskByte]; }
|
||||
auto readHalf(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskHalf]); }
|
||||
auto readWord(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskWord]); }
|
||||
auto readDouble(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskDouble]); }
|
||||
auto readQuad(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskQuad]); }
|
||||
|
||||
auto writeByte(u32 address, u8 value) -> void { *(u8*)&data[address & maskByte] = value; }
|
||||
auto writeHalf(u32 address, u16 value) -> void { *(u16*)&data[address & maskHalf] = bswap16(value); }
|
||||
auto writeWord(u32 address, u32 value) -> void { *(u32*)&data[address & maskWord] = bswap32(value); }
|
||||
auto writeDouble(u32 address, u64 value) -> void { *(u64*)&data[address & maskDouble] = bswap64(value); }
|
||||
auto writeQuad(u32 address, u128 value) -> void { *(u128*)&data[address & maskQuad] = bswap128(value); }
|
||||
|
||||
//N64 RSP allows unaligned memory accesses in certain cases
|
||||
auto readHalfUnaligned(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskByte]); }
|
||||
auto readWordUnaligned(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskByte]); }
|
||||
auto readDoubleUnaligned(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskByte]); }
|
||||
auto readQuadUnaligned(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskByte]); }
|
||||
|
||||
auto writeHalfUnaligned(u32 address, u16 value) -> void { *(u16*)&data[address & maskByte] = bswap16(value); }
|
||||
auto writeWordUnaligned(u32 address, u32 value) -> void { *(u32*)&data[address & maskByte] = bswap32(value); }
|
||||
auto writeDoubleUnaligned(u32 address, u64 value) -> void { *(u64*)&data[address & maskByte] = bswap64(value); }
|
||||
auto writeQuadUnaligned(u32 address, u128 value) -> void { *(u128*)&data[address & maskByte] = bswap128(value); }
|
||||
|
||||
//private:
|
||||
u8* data = nullptr;
|
||||
u32 size = 0;
|
||||
u32 maskByte = 0;
|
||||
u32 maskHalf = 0;
|
||||
u32 maskWord = 0;
|
||||
u32 maskDouble = 0;
|
||||
u32 maskQuad = 0;
|
||||
};
|
||||
|
||||
//00000000-7fffffff kuseg
|
||||
//80000000-9fffffff kseg0
|
||||
//a0000000-bfffffff kseg1
|
||||
//c0000000-dfffffff ksseg
|
||||
//e0000000-ffffffff kseg3
|
||||
|
||||
struct Bus {
|
||||
auto readByte(u32 address) -> u8;
|
||||
auto readHalf(u32 address) -> u16;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto readDouble(u32 address) -> u64;
|
||||
auto writeByte(u32 address, u8 data) -> void;
|
||||
auto writeHalf(u32 address, u16 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto writeDouble(u32 address, u64 data) -> void;
|
||||
|
||||
//bus.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
@@ -0,0 +1,16 @@
|
||||
auto MI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.interrupt = parent->append<Node::Notification>("Interrupt", "RCP");
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "MI");
|
||||
}
|
||||
|
||||
auto MI::Debugger::interrupt(string_view type) -> void {
|
||||
if(tracer.interrupt->enabled()) {
|
||||
tracer.interrupt->notify(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto MI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
static const vector<string> registerNames = {
|
||||
"MI_INIT_MODE",
|
||||
"MI_VERSION",
|
||||
"MI_INTR",
|
||||
"MI_INTR_MASK",
|
||||
};
|
||||
|
||||
auto MI::readIO(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//MI_INIT_MODE
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//MI_VERSION
|
||||
data.byte(0) = revision.io;
|
||||
data.byte(1) = revision.rac;
|
||||
data.byte(2) = revision.rdp;
|
||||
data.byte(3) = revision.rsp;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//MI_INTR
|
||||
data.bit(0) = irq.sp.line;
|
||||
data.bit(1) = irq.si.line;
|
||||
data.bit(2) = irq.ai.line;
|
||||
data.bit(3) = irq.vi.line;
|
||||
data.bit(4) = irq.pi.line;
|
||||
data.bit(5) = irq.dp.line;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//MI_INTR_MASK
|
||||
data.bit(0) = irq.sp.mask;
|
||||
data.bit(1) = irq.si.mask;
|
||||
data.bit(2) = irq.ai.mask;
|
||||
data.bit(3) = irq.vi.mask;
|
||||
data.bit(4) = irq.pi.mask;
|
||||
data.bit(5) = irq.dp.mask;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "MI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto MI::writeIO(u32 address, uint32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//MI_INIT_MODE
|
||||
if(data.bit(11)) mi.lower(MI::IRQ::DP);
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//MI_VERSION (read-only)
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//MI_INTR (read-only)
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//MI_INTR_MASK
|
||||
if(data.bit( 0)) irq.sp.mask = 0;
|
||||
if(data.bit( 1)) irq.sp.mask = 1;
|
||||
if(data.bit( 2)) irq.si.mask = 0;
|
||||
if(data.bit( 3)) irq.si.mask = 1;
|
||||
if(data.bit( 4)) irq.ai.mask = 0;
|
||||
if(data.bit( 5)) irq.ai.mask = 1;
|
||||
if(data.bit( 6)) irq.vi.mask = 0;
|
||||
if(data.bit( 7)) irq.vi.mask = 1;
|
||||
if(data.bit( 8)) irq.pi.mask = 0;
|
||||
if(data.bit( 9)) irq.pi.mask = 1;
|
||||
if(data.bit(10)) irq.dp.mask = 0;
|
||||
if(data.bit(11)) irq.dp.mask = 1;
|
||||
poll();
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "MI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
MI mi;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto MI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("MI");
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto MI::unload() -> void {
|
||||
node.reset();
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto MI::raise(IRQ source) -> void {
|
||||
if(debugger.tracer.interrupt->enabled()) {
|
||||
if(!irq.sp.line && source == IRQ::SP) debugger.interrupt("SP");
|
||||
if(!irq.si.line && source == IRQ::SI) debugger.interrupt("SI");
|
||||
if(!irq.ai.line && source == IRQ::AI) debugger.interrupt("AI");
|
||||
if(!irq.vi.line && source == IRQ::VI) debugger.interrupt("VI");
|
||||
if(!irq.pi.line && source == IRQ::PI) debugger.interrupt("PI");
|
||||
if(!irq.dp.line && source == IRQ::DP) debugger.interrupt("DP");
|
||||
}
|
||||
|
||||
switch(source) {
|
||||
case IRQ::SP: irq.sp.line = 1; break;
|
||||
case IRQ::SI: irq.si.line = 1; break;
|
||||
case IRQ::AI: irq.ai.line = 1; break;
|
||||
case IRQ::VI: irq.vi.line = 1; break;
|
||||
case IRQ::PI: irq.pi.line = 1; break;
|
||||
case IRQ::DP: irq.dp.line = 1; break;
|
||||
}
|
||||
poll();
|
||||
}
|
||||
|
||||
auto MI::lower(IRQ source) -> void {
|
||||
switch(source) {
|
||||
case IRQ::SP: irq.sp.line = 0; break;
|
||||
case IRQ::SI: irq.si.line = 0; break;
|
||||
case IRQ::AI: irq.ai.line = 0; break;
|
||||
case IRQ::VI: irq.vi.line = 0; break;
|
||||
case IRQ::PI: irq.pi.line = 0; break;
|
||||
case IRQ::DP: irq.dp.line = 0; break;
|
||||
}
|
||||
poll();
|
||||
}
|
||||
|
||||
auto MI::poll() -> void {
|
||||
bool line = 0;
|
||||
line |= irq.sp.line & irq.sp.mask;
|
||||
line |= irq.si.line & irq.si.mask;
|
||||
line |= irq.ai.line & irq.ai.mask;
|
||||
line |= irq.vi.line & irq.vi.mask;
|
||||
line |= irq.pi.line & irq.pi.mask;
|
||||
line |= irq.dp.line & irq.dp.mask;
|
||||
cpu.scc.cause.interruptPending.bit(2) = line;
|
||||
}
|
||||
|
||||
auto MI::power() -> void {
|
||||
irq.sp.line = 0; irq.sp.mask = 1;
|
||||
irq.si.line = 0; irq.si.mask = 1;
|
||||
irq.ai.line = 0; irq.ai.mask = 1;
|
||||
irq.vi.line = 0; irq.vi.mask = 1;
|
||||
irq.pi.line = 0; irq.pi.mask = 1;
|
||||
irq.dp.line = 0; irq.dp.mask = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//MIPS Interface
|
||||
|
||||
struct MI {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto interrupt(string_view) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification interrupt;
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//mi.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
enum class IRQ : uint { SP, SI, AI, VI, PI, DP };
|
||||
auto raise(IRQ) -> void;
|
||||
auto lower(IRQ) -> void;
|
||||
auto poll() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Interrupt {
|
||||
bool line;
|
||||
bool mask;
|
||||
};
|
||||
|
||||
struct IRQs {
|
||||
Interrupt sp;
|
||||
Interrupt si;
|
||||
Interrupt ai;
|
||||
Interrupt vi;
|
||||
Interrupt pi;
|
||||
Interrupt dp;
|
||||
} irq;
|
||||
|
||||
struct IO {
|
||||
} io;
|
||||
|
||||
struct Revision {
|
||||
static constexpr u8 io = 0x02; //I/O interface
|
||||
static constexpr u8 rac = 0x01; //RAMBUS ASIC cell
|
||||
static constexpr u8 rdp = 0x02; //Reality Display Processor
|
||||
static constexpr u8 rsp = 0x02; //Reality Signal Processor
|
||||
} revision;
|
||||
};
|
||||
|
||||
extern MI mi;
|
||||
@@ -0,0 +1,2 @@
|
||||
auto MI::serialize(serializer& s) -> void {
|
||||
}
|
||||
+50
-4
@@ -2,19 +2,65 @@
|
||||
//started: 2020-04-28
|
||||
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
#include <component/processor/vr4300/vr4300.hpp>
|
||||
#include <nmmintrin.h>
|
||||
using s128 = __m128i;
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
#include <ares/inline.hpp>
|
||||
struct Accuracy {
|
||||
//enable all accuracy flags
|
||||
static constexpr bool Reference = 0;
|
||||
|
||||
struct CPU {
|
||||
//exceptions when the CPU accesses unaligned memory addresses
|
||||
static constexpr bool AddressErrors = 0 | Reference;
|
||||
};
|
||||
};
|
||||
|
||||
struct Region {
|
||||
static inline auto NTSC() -> bool;
|
||||
static inline auto PAL() -> bool;
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
auto reset() -> void {
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
i64 clock;
|
||||
};
|
||||
|
||||
template<uint lo, uint hi, typename T>
|
||||
struct bitref {
|
||||
static constexpr T mask = ~0ull >> 64 - (hi - lo + 1) << lo;
|
||||
bitref(T& data) : data(data) {}
|
||||
operator T() const { return (data & mask) >> lo; }
|
||||
auto& operator=(T value) { return data = data & ~mask | value << lo & mask, *this; }
|
||||
auto& operator=(const bitref& value) { return operator=((T)value); }
|
||||
T& data;
|
||||
};
|
||||
|
||||
template<uint lo, typename T> auto bit(T& value) { return bitref<lo, lo, T>(value); }
|
||||
template<uint lo, uint hi, typename T> auto bit(T& value) { return bitref<lo, hi, T>(value); }
|
||||
|
||||
#include <n64/memory/memory.hpp>
|
||||
#include <n64/system/system.hpp>
|
||||
#include <n64/cartridge/cartridge.hpp>
|
||||
#include <n64/controller/controller.hpp>
|
||||
|
||||
#include <n64/mi/mi.hpp>
|
||||
#include <n64/vi/vi.hpp>
|
||||
#include <n64/ai/ai.hpp>
|
||||
#include <n64/pi/pi.hpp>
|
||||
#include <n64/ri/ri.hpp>
|
||||
#include <n64/si/si.hpp>
|
||||
#include <n64/rdram/rdram.hpp>
|
||||
#include <n64/cpu/cpu.hpp>
|
||||
#include <n64/rdp/rdp.hpp>
|
||||
#include <n64/rsp/rsp.hpp>
|
||||
#include <n64/memory/bus.hpp>
|
||||
}
|
||||
|
||||
#include <n64/interface/interface.hpp>
|
||||
@@ -0,0 +1,18 @@
|
||||
auto PI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "PI");
|
||||
|
||||
memory.ram = parent->append<Node::Memory>("PI RAM");
|
||||
memory.ram->setSize(64);
|
||||
memory.ram->setRead([&](uint32 address) -> uint8 {
|
||||
return pi.ram.readByte(address);
|
||||
});
|
||||
memory.ram->setWrite([&](uint32 address, uint8 data) -> void {
|
||||
return pi.ram.writeByte(address, data);
|
||||
});
|
||||
}
|
||||
|
||||
auto PI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
static const vector<string> registerNames = {
|
||||
"PI_DRAM_ADDRESS",
|
||||
"PI_PBUS_ADDRESS",
|
||||
"PI_READ_LENGTH",
|
||||
"PI_WRITE_LENGTH",
|
||||
"PI_STATUS",
|
||||
"PI_BSD_DOM1_LAT",
|
||||
"PI_BSD_DOM1_PWD",
|
||||
"PI_BSD_DOM1_PGS",
|
||||
"PI_BSD_DOM1_RLS",
|
||||
"PI_BSD_DOM2_LAT",
|
||||
"PI_BSD_DOM2_PWD",
|
||||
"PI_BSD_DOM2_PGS",
|
||||
"PI_BSD_DOM2_RLS",
|
||||
};
|
||||
|
||||
auto PI::readIO(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//PI_DRAM_ADDRESS
|
||||
data = io.dramAddress;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//PI_CART_ADDRESS
|
||||
data = io.pbusAddress;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//PI_READ_LENGTH
|
||||
data = io.readLength;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//PI_WRITE_LENGTH
|
||||
data = io.writeLength;
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//PI_STATUS
|
||||
data.bit(0) = io.dmaBusy;
|
||||
data.bit(1) = io.ioBusy;
|
||||
data.bit(2) = io.error;
|
||||
data.bit(3) = io.interrupt;
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//PI_BSD_DOM1_LAT
|
||||
data.bit(0,7) = bsd1.latency;
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//PI_BSD_DOM1_PWD
|
||||
data.bit(0,7) = bsd1.pulseWidth;
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//PI_BSD_DOM1_PGS
|
||||
data.bit(0,7) = bsd1.pageSize;
|
||||
}
|
||||
|
||||
if(address == 8) {
|
||||
//PI_BSD_DOM1_RLS
|
||||
data.bit(0,7) = bsd1.releaseDuration;
|
||||
}
|
||||
|
||||
if(address == 9) {
|
||||
//PI_BSD_DOM2_LAT
|
||||
data.bit(0,7) = bsd2.latency;
|
||||
}
|
||||
|
||||
if(address == 10) {
|
||||
//PI_BSD_DOM2_PWD
|
||||
data.bit(0,7) = bsd2.pulseWidth;
|
||||
}
|
||||
|
||||
if(address == 11) {
|
||||
//PI_BSD_DOM2_PGS
|
||||
data.bit(0,7) = bsd2.pageSize;
|
||||
}
|
||||
|
||||
if(address == 12) {
|
||||
//PI_BSD_DOM2_RLS
|
||||
data.bit(0,7) = bsd2.releaseDuration;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "PI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto PI::writeIO(u32 address, uint32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
//only PI_STATUS can be written while PI is busy
|
||||
if(address != 4 && (io.dmaBusy || io.ioBusy)) {
|
||||
io.error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(address == 0) {
|
||||
//PI_DRAM_ADDRESS
|
||||
io.dramAddress = uint24(data) & ~7;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//PI_PBUS_ADDRESS
|
||||
io.pbusAddress = uint29(data) & ~1;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//PI_READ_LENGTH
|
||||
io.readLength = (uint24(data) | 7) + 1;
|
||||
for(u32 address = 0; address < io.readLength; address += 2) {
|
||||
u16 data = rdram.ram.readHalf(io.dramAddress + address);
|
||||
bus.writeHalf(io.pbusAddress + address, data);
|
||||
}
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//PI_WRITE_LENGTH
|
||||
io.writeLength = (uint24(data) | 7) + 1;
|
||||
for(u32 address = 0; address < io.writeLength; address += 2) {
|
||||
u16 data = bus.readHalf(io.pbusAddress + address);
|
||||
rdram.ram.writeHalf(io.dramAddress + address, data);
|
||||
}
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//PI_STATUS
|
||||
if(data.bit(0)) io.error = 0;
|
||||
if(data.bit(1)) io.interrupt = 0, mi.lower(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//PI_BSD_DOM1_LAT
|
||||
bsd1.latency = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//PI_BSD_DOM1_PWD
|
||||
bsd1.pulseWidth = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//PI_BSD_DOM1_PGS
|
||||
bsd1.pageSize = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 8) {
|
||||
//PI_BSD_DOM1_RLS
|
||||
bsd1.releaseDuration = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 9) {
|
||||
//PI_BSD_DOM2_LAT
|
||||
bsd2.latency = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 10) {
|
||||
//PI_BSD_DOM2_PWD
|
||||
bsd2.pulseWidth = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 11) {
|
||||
//PI_BSD_DOM2_PGS
|
||||
bsd2.pageSize = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 12) {
|
||||
//PI_BSD_DOM2_RLS
|
||||
bsd2.releaseDuration = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "PI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
PI pi;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto PI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("PI");
|
||||
rom.allocate(0x7c0);
|
||||
ram.allocate(0x040);
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto PI::unload() -> void {
|
||||
rom.reset();
|
||||
ram.reset();
|
||||
node = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto PI::power() -> void {
|
||||
rom.fill();
|
||||
ram.fill();
|
||||
io = {};
|
||||
bsd1 = {};
|
||||
bsd2 = {};
|
||||
|
||||
string iplrom = cartridge.region() == "NTSC" ? "pif.ntsc.rom" : "pif.pal.rom";
|
||||
iplrom = "pif.rom";
|
||||
if(auto fp = platform->open(node, iplrom, File::Read, File::Required)) {
|
||||
for(u32 offset = 0; offset < 0x7c0; offset++) {
|
||||
rom.writeByte(offset, fp->read());
|
||||
}
|
||||
}
|
||||
|
||||
//write CIC seeds into PIF RAM so that cartridge checksum function passes
|
||||
string cic = cartridge.cic();
|
||||
if(1) { //power
|
||||
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") ram.writeWord(0x24, 0x00043f3f);
|
||||
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") ram.writeWord(0x24, 0x00003f3f);
|
||||
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") ram.writeWord(0x24, 0x0000783f);
|
||||
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") ram.writeWord(0x24, 0x0000913f);
|
||||
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") ram.writeWord(0x24, 0x0000853f);
|
||||
}
|
||||
|
||||
if(0) { //reset
|
||||
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") ram.writeWord(0x24, 0x00063f3f);
|
||||
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") ram.writeWord(0x24, 0x00023f3f);
|
||||
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") ram.writeWord(0x24, 0x0002783f);
|
||||
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") ram.writeWord(0x24, 0x0002913f);
|
||||
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") ram.writeWord(0x24, 0x0002853f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//Peripheral Interface
|
||||
|
||||
struct PI {
|
||||
Node::Component node;
|
||||
Memory rom;
|
||||
Memory ram;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
|
||||
struct Memory {
|
||||
Node::Memory ram;
|
||||
} memory;
|
||||
} debugger;
|
||||
|
||||
//pi.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
uint1 dmaBusy = 0;
|
||||
uint1 ioBusy = 0;
|
||||
uint1 error = 0;
|
||||
uint1 interrupt = 0;
|
||||
uint32 dramAddress = 0;
|
||||
uint32 pbusAddress = 0;
|
||||
uint32 readLength = 0;
|
||||
uint32 writeLength = 0;
|
||||
} io;
|
||||
|
||||
struct BSD {
|
||||
uint8 latency = 0;
|
||||
uint8 pulseWidth = 0;
|
||||
uint8 pageSize = 0;
|
||||
uint8 releaseDuration = 0;
|
||||
} bsd1, bsd2;
|
||||
};
|
||||
|
||||
extern PI pi;
|
||||
@@ -0,0 +1,2 @@
|
||||
auto PI::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
auto RDP::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.command = parent->append<Node::Notification>("Command", "RDP");
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "RDP");
|
||||
}
|
||||
|
||||
auto RDP::Debugger::command(string_view message) -> void {
|
||||
if(tracer.command->enabled()) {
|
||||
tracer.command->notify(message);
|
||||
}
|
||||
}
|
||||
|
||||
auto RDP::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
static const vector<string> registerNamesSCC = {
|
||||
"DPC_START",
|
||||
"DPC_END",
|
||||
"DPC_CURRENT",
|
||||
"DPC_STATUS",
|
||||
"DPC_CLOCK",
|
||||
"DPC_BUSY",
|
||||
"DPC_PIPE_BUSY",
|
||||
"DPC_TMEM_BUSY",
|
||||
};
|
||||
|
||||
auto RDP::readSCC(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//DPC_START
|
||||
data.bit(0,23) = command.start;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPC_END
|
||||
data.bit(0,23) = command.end;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//DPC_CURRENT
|
||||
data.bit(0,23) = command.current;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//DPC_STATUS
|
||||
data.bit( 0) = command.source;
|
||||
data.bit( 1) = command.freeze;
|
||||
data.bit( 2) = command.flush;
|
||||
data.bit( 3) = 0; //start gclk?
|
||||
data.bit( 4) = command.tmemBusy > 0;
|
||||
data.bit( 5) = command.pipeBusy > 0;
|
||||
data.bit( 6) = command.bufferBusy > 0;
|
||||
data.bit( 7) = command.ready;
|
||||
data.bit( 8) = 0; //DMA busy
|
||||
data.bit( 9) = 0; //end valid
|
||||
data.bit(10) = 0; //start valid
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//DPC_CLOCK
|
||||
data.bit(0,23) = command.clock;
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//DPC_BUSY
|
||||
data.bit(0,23) = command.bufferBusy;
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//DPC_PIPE_BUSY
|
||||
data.bit(0,23) = command.pipeBusy;
|
||||
}
|
||||
|
||||
if(data == 7) {
|
||||
//DPC_TMEM_BUSY
|
||||
data.bit(0,23) = command.tmemBusy;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNamesSCC(address, "DPC_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RDP::writeSCC(u32 address, uint32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//DPC_START
|
||||
command.start = data.bit(0,23) & ~7;
|
||||
command.current = command.start;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPC_END
|
||||
command.end = data.bit(0,23) & ~7;
|
||||
command.freeze = 0;
|
||||
render();
|
||||
command.current = command.end;
|
||||
command.ready = 1;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//DPC_CURRENT (read-only)
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//DPC_STATUS
|
||||
if(data.bit(0)) command.source = 0;
|
||||
if(data.bit(1)) command.source = 1;
|
||||
if(data.bit(2)) command.freeze = 0;
|
||||
//if(data.bit(3)) command.freeze = 1;
|
||||
if(data.bit(4)) command.flush = 0;
|
||||
if(data.bit(5)) command.flush = 1;
|
||||
if(data.bit(6)) command.tmemBusy = 0;
|
||||
if(data.bit(7)) command.pipeBusy = 0;
|
||||
if(data.bit(8)) command.bufferBusy = 0;
|
||||
if(data.bit(9)) command.clock = 0;
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//DPC_CLOCK (read-only)
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//DPC_BUSY (read-only)
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//DPC_PIPE_BUSY (read-only)
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//DPC_TMEM_BUSY (read-only)
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNamesSCC(address, "DPC_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
static const vector<string> registerNames = {
|
||||
"DPS_TBIST",
|
||||
"DPS_TEST_MODE",
|
||||
"DPS_BUFTEST_ADDR",
|
||||
"DPS_BUFTEST_DATA",
|
||||
};
|
||||
|
||||
auto RDP::readIO(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//DPS_TBIST
|
||||
data.bit(0) = io.bist.check;
|
||||
data.bit(1) = io.bist.go;
|
||||
data.bit(2) = io.bist.done;
|
||||
data.bit(3,10) = io.bist.fail;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPS_TEST_MODE
|
||||
data.bit(0) = io.test.enable;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//DPS_BUFTEST_ADDR
|
||||
data.bit(0,6) = io.test.address;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//DPS_BUFTEST_DATA
|
||||
data.bit(0,31) = io.test.data;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "DPS_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RDP::writeIO(u32 address, uint32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//DPS_TBIST
|
||||
io.bist.check = data.bit(0);
|
||||
io.bist.go = data.bit(1);
|
||||
if(data.bit(2)) io.bist.done = 0;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPS_TEST_MODE
|
||||
io.test.enable = data.bit(0);
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//DPS_BUFTEST_ADDR
|
||||
io.test.address = data.bit(0,6);
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//DPS_BUFTEST_DATA
|
||||
io.test.data = data.bit(0,31);
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "DPS_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
+12
-14
@@ -3,37 +3,35 @@
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
RDP rdp;
|
||||
#include "render.cpp"
|
||||
#include "io.cpp"
|
||||
#include "io-scc.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RDP::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("RDP");
|
||||
|
||||
screen = node->append<Node::Screen>("Screen");
|
||||
screen->colors(1, [&](uint32 color) -> uint64 { return ~0; });
|
||||
screen->setSize(640, 480);
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto RDP::unload() -> void {
|
||||
node = {};
|
||||
screen = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto RDP::main() -> void {
|
||||
scheduler.exit(Event::Frame);
|
||||
step(1);
|
||||
step(93'750'000);
|
||||
}
|
||||
|
||||
auto RDP::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
auto RDP::refresh() -> void {
|
||||
screen->refresh(output, 640 * sizeof(uint32), 640, 480);
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
auto RDP::power() -> void {
|
||||
Thread::create(60, {&RDP::main, this});
|
||||
Thread::reset();
|
||||
command = {};
|
||||
set = {};
|
||||
io = {};
|
||||
}
|
||||
|
||||
}
|
||||
+86
-5
@@ -2,7 +2,18 @@
|
||||
|
||||
struct RDP : Thread {
|
||||
Node::Component node;
|
||||
Node::Screen screen;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto command(string_view) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification command;
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//rdp.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -10,15 +21,85 @@ struct RDP : Thread {
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//render.cpp
|
||||
auto render() -> void;
|
||||
auto noOperation() -> void;
|
||||
auto invalidOperation() -> void;
|
||||
auto syncLoad() -> void;
|
||||
auto syncPipe() -> void;
|
||||
auto syncTile() -> void;
|
||||
auto syncFull() -> void;
|
||||
auto textureRectangle(
|
||||
uint12 xl, uint12 yl, uint3 tile, uint12 xh, uint12 yh,
|
||||
uint16 s, uint16 t, uint16 dsdx, uint16 dsdy
|
||||
) -> void;
|
||||
auto textureRectangleFlip(
|
||||
uint12 xl, uint12 yl, uint3 tile, uint12 xh, uint12 yh,
|
||||
uint16 s, uint16 t, uint16 dsdx, uint16 dsdy
|
||||
) -> void;
|
||||
auto fillRectangle(uint12 xl, uint12 yl, uint12 xh, uint12 yh) -> void;
|
||||
auto setFillColor(uint32 color) -> void;
|
||||
auto setTextureImage(uint3 format, uint2 size, uint10 width, uint26 dramAddress) -> void;
|
||||
auto setColorImage(uint3 format, uint2 size, uint10 width, uint26 dramAddress) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
|
||||
//io-scc.cpp
|
||||
auto readSCC(u32 address) -> u32;
|
||||
auto writeSCC(u32 address, uint32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
uint32 output[640 * 480];
|
||||
struct Command {
|
||||
uint24 start = 0;
|
||||
uint24 end = 0;
|
||||
uint24 current = 0;
|
||||
uint24 clock = 0;
|
||||
uint24 bufferBusy = 0;
|
||||
uint24 pipeBusy = 0;
|
||||
uint24 tmemBusy = 0;
|
||||
uint1 source = 0; //0 = RDRAM, 1 = DMEM
|
||||
uint1 freeze = 0;
|
||||
uint1 flush = 0;
|
||||
uint1 ready = 1;
|
||||
} command;
|
||||
|
||||
struct Set {
|
||||
struct Fill {
|
||||
uint32 color = 0;
|
||||
} fill;
|
||||
struct Texture {
|
||||
uint3 format = 0;
|
||||
uint2 size = 0;
|
||||
uint10 width = 0;
|
||||
uint26 dramAddress = 0;
|
||||
} texture;
|
||||
struct Color {
|
||||
uint3 format = 0;
|
||||
uint2 size = 0;
|
||||
uint10 width = 0;
|
||||
uint26 dramAddress = 0;
|
||||
} color;
|
||||
} set;
|
||||
|
||||
struct IO {
|
||||
struct BIST {
|
||||
uint1 check = 0;
|
||||
uint1 go = 0;
|
||||
uint1 done = 0;
|
||||
uint8 fail = 0x00;
|
||||
} bist;
|
||||
struct Test {
|
||||
uint1 enable = 0;
|
||||
uint7 address = 0;
|
||||
uint32 data = 0;
|
||||
} test;
|
||||
} io;
|
||||
};
|
||||
|
||||
extern RDP rdp;
|
||||
@@ -0,0 +1,382 @@
|
||||
static const vector<string> commandNames = {
|
||||
"No_Operation", "Invalid_01", "Invalid_02", "Invalid_03",
|
||||
"Invalid_04", "Invalid_05", "Invalid_06", "Invalid_07",
|
||||
"Unshaded_Triangle",
|
||||
"Unshaded_Zbuffer_Triangle",
|
||||
"Texture_Triangle",
|
||||
"Texture_Zbuffer_Triangle",
|
||||
"Shaded_Triangle",
|
||||
"Shaded_Zbuffer_Triangle",
|
||||
"Shaded_Texture_Triangle",
|
||||
"Shaded_Texture_Zbuffer_Triangle",
|
||||
"Invalid_10", "Invalid_11", "Invalid_12", "Invalid_13",
|
||||
"Invalid_14", "Invalid_15", "Invalid_16", "Invalid_17",
|
||||
"Invalid_18", "Invalid_19", "Invalid_1a", "Invalid_1b",
|
||||
"Invalid_1c", "Invalid_1d", "Invalid_1e", "Invalid_1f",
|
||||
"Invalid_20", "Invalid_21", "Invalid_22", "Invalid_23",
|
||||
"Texture_Rectangle",
|
||||
"Texture_Rectangle_Flip",
|
||||
"Sync_Load",
|
||||
"Sync_Pipe",
|
||||
"Sync_Tile",
|
||||
"Sync_Full",
|
||||
"Set_Key_GB",
|
||||
"Set_Key_R",
|
||||
"Set_Convert",
|
||||
"Set_Scissor",
|
||||
"Set_Primitive_Depth",
|
||||
"Set_Other_Modes",
|
||||
"Load_Texture_LUT",
|
||||
"Invalid_31",
|
||||
"Set_Tile_Size",
|
||||
"Load_Block",
|
||||
"Load_Tile",
|
||||
"Set_Tile",
|
||||
"Fill_Rectangle",
|
||||
"Set_Fill_Color",
|
||||
"Set_Fog_Color",
|
||||
"Set_Blend_Color",
|
||||
"Set_Primitive_Color",
|
||||
"Set_Environment_Color",
|
||||
"Set_Combine_Mode",
|
||||
"Set_Texture_Image",
|
||||
"Set_Mask_Image",
|
||||
"Set_Color_Image",
|
||||
};
|
||||
|
||||
auto RDP::render() -> void {
|
||||
auto& memory = !command.source ? rdram.ram : rsp.dmem;
|
||||
auto fetch = [&]() -> u64 {
|
||||
u64 op = memory.readDoubleUnaligned(command.current);
|
||||
command.current += 8;
|
||||
return op;
|
||||
};
|
||||
|
||||
while(command.current < command.end) {
|
||||
u64 op = fetch();
|
||||
|
||||
if(debugger.tracer.command->enabled()) {
|
||||
debugger.command({hex(op, 16L), " ", commandNames(op >> 56 & 0x3f, "Invalid")});
|
||||
}
|
||||
|
||||
switch(op >> 56 & 0x3f) {
|
||||
|
||||
case 0x00: {
|
||||
noOperation();
|
||||
} break;
|
||||
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07: {
|
||||
invalidOperation();
|
||||
} break;
|
||||
|
||||
case 0x08: {
|
||||
command.current += 8 * 3; //edge
|
||||
//unshadedTriangle();
|
||||
} break;
|
||||
|
||||
case 0x09: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//unshadedZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0a: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //texture
|
||||
//textureTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0b: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //texture
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//textureZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0c: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
//shadedTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0d: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//shadedZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0e: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
command.current += 8 * 8; //texture
|
||||
//shadedTextureTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0f: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
command.current += 8 * 8; //texture
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//shadedTextureZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case 0x14:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
case 0x17:
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x1f:
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23: {
|
||||
invalidOperation();
|
||||
} break;
|
||||
|
||||
case 0x24: {
|
||||
auto xl = uint12(op >> 44);
|
||||
auto yl = uint12(op >> 32);
|
||||
auto tile = uint3(op >> 24);
|
||||
auto xh = uint12(op >> 12);
|
||||
auto yh = uint12(op >> 0);
|
||||
op = fetch();
|
||||
auto s = uint16(op >> 48);
|
||||
auto t = uint16(op >> 32);
|
||||
auto dsdx = uint16(op >> 16);
|
||||
auto dsdy = uint16(op >> 0);
|
||||
textureRectangle(xl, yl, tile, xh, yh, s, t, dsdx, dsdy);
|
||||
} break;
|
||||
|
||||
case 0x25: {
|
||||
auto xl = uint12(op >> 44);
|
||||
auto yl = uint12(op >> 32);
|
||||
auto tile = uint3(op >> 24);
|
||||
auto xh = uint12(op >> 12);
|
||||
auto yh = uint12(op >> 0);
|
||||
op = fetch();
|
||||
auto s = uint16(op >> 48);
|
||||
auto t = uint16(op >> 32);
|
||||
auto dsdx = uint16(op >> 16);
|
||||
auto dsdy = uint16(op >> 0);
|
||||
textureRectangleFlip(xl, yl, tile, xh, yh, s, t, dsdx, dsdy);
|
||||
} break;
|
||||
|
||||
case 0x26: {
|
||||
syncLoad();
|
||||
} break;
|
||||
|
||||
case 0x27: {
|
||||
syncPipe();
|
||||
} break;
|
||||
|
||||
case 0x28: {
|
||||
syncTile();
|
||||
} break;
|
||||
|
||||
case 0x29: {
|
||||
syncFull();
|
||||
} break;
|
||||
|
||||
case 0x2a: {
|
||||
//setKeyGB();
|
||||
} break;
|
||||
|
||||
case 0x2b: {
|
||||
//setKeyR();
|
||||
} break;
|
||||
|
||||
case 0x2c: {
|
||||
//setConvert();
|
||||
} break;
|
||||
|
||||
case 0x2d: {
|
||||
//setScissor();
|
||||
} break;
|
||||
|
||||
case 0x2e: {
|
||||
//setPrimitiveDepth();
|
||||
} break;
|
||||
|
||||
case 0x2f: {
|
||||
//setOtherModes();
|
||||
} break;
|
||||
|
||||
case 0x30: {
|
||||
//loadTextureLUT();
|
||||
} break;
|
||||
|
||||
case 0x31: {
|
||||
invalidOperation();
|
||||
} break;
|
||||
|
||||
case 0x32: {
|
||||
//setTileSize();
|
||||
} break;
|
||||
|
||||
case 0x33: {
|
||||
//loadBlock();
|
||||
} break;
|
||||
|
||||
case 0x34: {
|
||||
//loadTile();
|
||||
} break;
|
||||
|
||||
case 0x35: {
|
||||
//setTile();
|
||||
} break;
|
||||
|
||||
case 0x36: {
|
||||
auto xl = uint12(op >> 44);
|
||||
auto yl = uint12(op >> 32);
|
||||
auto xh = uint12(op >> 12);
|
||||
auto yh = uint12(op >> 0);
|
||||
fillRectangle(xl, yl, xh, yh);
|
||||
} break;
|
||||
|
||||
case 0x37: {
|
||||
auto color = uint32(op >> 0);
|
||||
setFillColor(color);
|
||||
} break;
|
||||
|
||||
case 0x38: {
|
||||
//setFogColor();
|
||||
} break;
|
||||
|
||||
case 0x39: {
|
||||
//setBlendColor();
|
||||
} break;
|
||||
|
||||
case 0x3a: {
|
||||
//setPrimitiveColor();
|
||||
} break;
|
||||
|
||||
case 0x3b: {
|
||||
//setEnvironmentColor();
|
||||
} break;
|
||||
|
||||
case 0x3c: {
|
||||
//setCombineMode();
|
||||
} break;
|
||||
|
||||
case 0x3d: {
|
||||
auto format = uint3(op >> 53);
|
||||
auto size = uint2(op >> 51);
|
||||
auto width = uint10(op >> 32);
|
||||
auto dramAddress = uint26(op >> 0);
|
||||
setTextureImage(format, size, width, dramAddress);
|
||||
} break;
|
||||
|
||||
case 0x3e: {
|
||||
//setMaskImage();
|
||||
} break;
|
||||
|
||||
case 0x3f: {
|
||||
auto format = uint3(op >> 53);
|
||||
auto size = uint2(op >> 51);
|
||||
auto width = uint10(op >> 32);
|
||||
auto dramAddress = uint26(op >> 0);
|
||||
setColorImage(format, size, width, dramAddress);
|
||||
} break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//0x00
|
||||
auto RDP::noOperation() -> void {
|
||||
}
|
||||
|
||||
//0x01-0x07; 0x10-0x23; 0x31
|
||||
auto RDP::invalidOperation() -> void {
|
||||
}
|
||||
|
||||
//0x24
|
||||
auto RDP::textureRectangle(
|
||||
uint12 xl, uint12 yl, uint3 tile, uint12 xh, uint12 yh,
|
||||
uint16 s, uint16 t, uint16 dsdx, uint16 dsdy
|
||||
) -> void {
|
||||
}
|
||||
|
||||
//0x25
|
||||
auto RDP::textureRectangleFlip(
|
||||
uint12 xl, uint12 yl, uint3 tile, uint12 xh, uint12 yh,
|
||||
uint16 s, uint16 t, uint16 dsdx, uint16 dsdy
|
||||
) -> void {
|
||||
}
|
||||
|
||||
//0x26
|
||||
auto RDP::syncLoad() -> void {
|
||||
}
|
||||
|
||||
//0x27
|
||||
auto RDP::syncPipe() -> void {
|
||||
}
|
||||
|
||||
//0x28
|
||||
auto RDP::syncTile() -> void {
|
||||
}
|
||||
|
||||
//0x29
|
||||
auto RDP::syncFull() -> void {
|
||||
mi.raise(MI::IRQ::DP);
|
||||
}
|
||||
|
||||
//0x36
|
||||
auto RDP::fillRectangle(uint12 xl, uint12 yl, uint12 xh, uint12 yh) -> void {
|
||||
u32 depth = vi.io.colorDepth == 2 ? 2 : 4;
|
||||
u32 step = vi.io.colorDepth == 2 ? 2 : 1;
|
||||
u32 shift = 2;
|
||||
|
||||
xl >>= shift;
|
||||
yl >>= shift;
|
||||
xh >>= shift;
|
||||
yh >>= shift;
|
||||
|
||||
u32 base = set.color.dramAddress;
|
||||
for(u32 y = yh; y <= yl; y++) {
|
||||
u32 line = base + y * vi.io.width * depth;
|
||||
u32 address = line + xh * depth;
|
||||
for(u32 x = xh; x <= xl; x += step) {
|
||||
//rdram.ram.writeWord(address, set.fill.color);
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//0x37
|
||||
auto RDP::setFillColor(uint32 color) -> void {
|
||||
set.fill.color = color;
|
||||
}
|
||||
|
||||
//0x3d
|
||||
auto RDP::setTextureImage(uint3 format, uint2 size, uint10 width, uint26 dramAddress) -> void {
|
||||
set.texture.format = format;
|
||||
set.texture.size = size;
|
||||
set.texture.width = width;
|
||||
set.texture.dramAddress = dramAddress;
|
||||
}
|
||||
|
||||
//0x3f
|
||||
auto RDP::setColorImage(uint3 format, uint2 size, uint10 width, uint26 dramAddress) -> void {
|
||||
set.color.format = format;
|
||||
set.color.size = size;
|
||||
set.color.width = width;
|
||||
set.color.dramAddress = dramAddress;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
auto RDRAM::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "RDRAM");
|
||||
|
||||
memory.ram = parent->append<Node::Memory>("RDRAM");
|
||||
memory.ram->setSize(4_MiB + 4_MiB);
|
||||
memory.ram->setRead([&](uint32 address) -> uint8 {
|
||||
return rdram.ram.readByte(address);
|
||||
});
|
||||
memory.ram->setWrite([&](uint32 address, uint8 data) -> void {
|
||||
return rdram.ram.writeByte(address, data);
|
||||
});
|
||||
}
|
||||
|
||||
auto RDRAM::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
static const vector<string> registerNames = {
|
||||
"RDRAM_CONFIG",
|
||||
"RDRAM_DEVICE_ID",
|
||||
"RDRAM_DELAY",
|
||||
"RDRAM_MODE",
|
||||
"RDRAM_REF_INTERVAL",
|
||||
"RDRAM_REF_ROW",
|
||||
"RDRAM_RAS_INTERVAL",
|
||||
"RDRAM_MIN_INTERVAL",
|
||||
"RDRAM_ADDR_SELECT",
|
||||
"RDRAM_DEVICE_MANUF",
|
||||
};
|
||||
|
||||
auto RDRAM::readIO(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
u32 data = 0;
|
||||
|
||||
if(address == 0) {
|
||||
//RDRAM_CONFIG
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//RDRAM_DEVICE_ID
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//RDRAM_DELAY
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//RDRAM_MODE
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//RDRAM_REF_INTERVAL
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//RDRAM_REF_ROW
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//RDRAM_RAS_INTERVAL
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//RDRAM_MIN_INTERVAL
|
||||
}
|
||||
|
||||
if(address == 8) {
|
||||
//RDRAM_ADDR_SELECT
|
||||
}
|
||||
|
||||
if(address == 9) {
|
||||
//RDRAM_DEVICE_MANUF
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "RDRAM_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RDRAM::writeIO(u32 address, u32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//RDRAM_CONFIG
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//RDRAM_DEVICE_ID
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//RDRAM_DELAY
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//RDRAM_MODE
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//RDRAM_REF_INTERVAL
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//RDRAM_REF_ROW
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//RDRAM_RAS_INTERVAL
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//RDRAM_MIN_INTERVAL
|
||||
}
|
||||
|
||||
if(address == 8) {
|
||||
//RDRAM_ADDR_SELECT
|
||||
}
|
||||
|
||||
if(address == 9) {
|
||||
//RDRAM_DEVICE_MANUF
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "RDRAM_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
RDRAM rdram;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RDRAM::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("RDRAM");
|
||||
|
||||
//4_MiB internal
|
||||
//4_MiB expansion pak
|
||||
ram.allocate(4_MiB + 4_MiB);
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto RDRAM::unload() -> void {
|
||||
ram.reset();
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto RDRAM::power() -> void {
|
||||
ram.fill();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//RAMBUS RAM
|
||||
|
||||
struct RDRAM {
|
||||
Node::Component node;
|
||||
Memory ram;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
|
||||
struct Memory {
|
||||
Node::Memory ram;
|
||||
} memory;
|
||||
} debugger;
|
||||
|
||||
//rdram.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
} io;
|
||||
};
|
||||
|
||||
extern RDRAM rdram;
|
||||
@@ -0,0 +1,2 @@
|
||||
auto RDRAM::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
auto RI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "RI");
|
||||
}
|
||||
|
||||
auto RI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
static const vector<string> registerNames = {
|
||||
"RI_MODE",
|
||||
"RI_CONFIG",
|
||||
"RI_CURRENT_LOAD",
|
||||
"RI_SELECT",
|
||||
"RI_REFRESH",
|
||||
"RI_LATENCY",
|
||||
"RI_RERROR",
|
||||
"RI_WERROR",
|
||||
};
|
||||
|
||||
auto RI::readIO(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = 0;
|
||||
|
||||
if(address == 0) {
|
||||
//RI_MODE
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//RI_CONFIG
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//RI_CURRENT_LOAD
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//RI_SELECT
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//RI_REFRESH
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//RI_LATENCY
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//RI_RERROR
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//RI_WERROR
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "RI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RI::writeIO(u32 address, u32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//RI_MODE
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//RI_CONFIG
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//RI_CURRENT_LOAD
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//RI_SELECT
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//RI_REFRESH
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//RI_LATENCY
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//RI_RERROR
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//RI_WERROR
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "RI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
RI ri;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("RI");
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto RI::unload() -> void {
|
||||
node = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto RI::power() -> void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//RDRAM Interface
|
||||
|
||||
struct RI {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//ri.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern RI ri;
|
||||
@@ -0,0 +1,2 @@
|
||||
auto RI::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "registers.hpp"
|
||||
#include "instruction.cpp"
|
||||
#include "cpu-instructions.cpp"
|
||||
#include "scc-instructions.cpp"
|
||||
#include "vu-instructions.cpp"
|
||||
#include "disassembler.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RSP::powerCore() -> void {
|
||||
for(uint n : range(32)) GPR[n].u32 = 0;
|
||||
PC = 0;
|
||||
branch.reset();
|
||||
for(uint n : range(32)) VPR[n].u128 = 0;
|
||||
ACCH = zero;
|
||||
ACCM = zero;
|
||||
ACCL = zero;
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VCCH = zero;
|
||||
VCCL = zero;
|
||||
VCE = zero;
|
||||
DIVIN = 0;
|
||||
DIVOUT = 0;
|
||||
DIVDP = 0;
|
||||
|
||||
reciprocals[0] = u16(~0);
|
||||
for(u16 index : range(1, 512)) {
|
||||
u64 a = index + 512;
|
||||
u64 b = (u64(1) << 34) / a;
|
||||
reciprocals[index] = u16(b + 1 >> 8);
|
||||
}
|
||||
|
||||
for(u16 index : range(0, 512)) {
|
||||
u64 a = index + 512 >> (index % 2 == 1);
|
||||
u64 b = 1 << 17;
|
||||
//find the largest b where b < 1.0 / sqrt(a)
|
||||
while(a * (b + 1) * (b + 1) < (u64(1) << 44)) b++;
|
||||
inverseSquareRoots[index] = u16(b >> 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//{
|
||||
//core.cpp
|
||||
auto powerCore() -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto instruction() -> void;
|
||||
auto instructionDEBUG() -> void;
|
||||
auto instructionEXECUTE() -> void;
|
||||
auto instructionSPECIAL() -> void;
|
||||
auto instructionREGIMM() -> void;
|
||||
auto instructionCOP0() -> void;
|
||||
auto instructionCOP2() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serializeCore(serializer&) -> void;
|
||||
|
||||
#include "pipeline.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include "scc.hpp"
|
||||
#include "vu.hpp"
|
||||
#include "disassembler.hpp"
|
||||
//};
|
||||
@@ -0,0 +1,152 @@
|
||||
auto RSP::instructionADDIU() -> void {
|
||||
RT.u32 = i32(RS.u32 + IMMi16);
|
||||
}
|
||||
|
||||
auto RSP::instructionADDU() -> void {
|
||||
RD.u32 = i32(RS.u32 + RT.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionAND() -> void {
|
||||
RD.u32 = RS.u32 & RT.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionANDI() -> void {
|
||||
RT.u32 = RS.u32 & IMMu16;
|
||||
}
|
||||
|
||||
auto RSP::instructionB(bool take) -> void {
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBAL(bool take) -> void {
|
||||
RA.u32 = i32(PC + 8);
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBREAK() -> void {
|
||||
status.halted = 1;
|
||||
status.broken = 1;
|
||||
if(status.interruptOnBreak) mi.raise(MI::IRQ::SP);
|
||||
}
|
||||
|
||||
auto RSP::instructionCACHE() -> void {
|
||||
//todo
|
||||
}
|
||||
|
||||
auto RSP::instructionJ() -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (IMMu26 << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionJAL() -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (IMMu26 << 2));
|
||||
RA.u32 = i32(PC + 8);
|
||||
}
|
||||
|
||||
auto RSP::instructionJALR() -> void {
|
||||
branch.take(RS.u32);
|
||||
RD.u32 = i32(PC + 8);
|
||||
}
|
||||
|
||||
auto RSP::instructionJR() -> void {
|
||||
branch.take(RS.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionLB() -> void {
|
||||
RT.u32 = i8(dmem.readByte(RS.u32 + IMMi16));
|
||||
}
|
||||
|
||||
auto RSP::instructionLBU() -> void {
|
||||
RT.u32 = u8(dmem.readByte(RS.u32 + IMMi16));
|
||||
}
|
||||
|
||||
auto RSP::instructionLH() -> void {
|
||||
RT.u32 = i16(dmem.readHalfUnaligned(RS.u32 + IMMi16));
|
||||
}
|
||||
|
||||
auto RSP::instructionLHU() -> void {
|
||||
RT.u32 = u16(dmem.readHalfUnaligned(RS.u32 + IMMi16));
|
||||
}
|
||||
|
||||
auto RSP::instructionLUI() -> void {
|
||||
RT.u32 = i32(IMMu16 << 16);
|
||||
}
|
||||
|
||||
auto RSP::instructionLW() -> void {
|
||||
RT.u32 = i32(dmem.readWordUnaligned(RS.u32 + IMMi16));
|
||||
}
|
||||
|
||||
auto RSP::instructionNOR() -> void {
|
||||
RD.u32 = ~(RS.u32 | RT.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionOR() -> void {
|
||||
RD.u32 = RS.u32 | RT.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionORI() -> void {
|
||||
RT.u32 = RS.u32 | IMMu16;
|
||||
}
|
||||
|
||||
auto RSP::instructionSB() -> void {
|
||||
dmem.writeByte(RS.u32 + IMMi16, RT.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionSH() -> void {
|
||||
dmem.writeHalfUnaligned(RS.u32 + IMMi16, RT.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionSLL() -> void {
|
||||
RD.u32 = i32(RT.u32 << SA);
|
||||
}
|
||||
|
||||
auto RSP::instructionSLLV() -> void {
|
||||
RD.u32 = i32(RT.u32 << (RS.u32 & 31));
|
||||
}
|
||||
|
||||
auto RSP::instructionSLT() -> void {
|
||||
RD.u32 = RS.i32 < RT.i32;
|
||||
}
|
||||
|
||||
auto RSP::instructionSLTI() -> void {
|
||||
RT.u32 = RS.i32 < i32(IMMi16);
|
||||
}
|
||||
|
||||
auto RSP::instructionSLTIU() -> void {
|
||||
RT.u32 = RS.u32 < u32(IMMu16);
|
||||
}
|
||||
|
||||
auto RSP::instructionSLTU() -> void {
|
||||
RD.u32 = RS.u32 < RT.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionSRA() -> void {
|
||||
RD.u32 = RT.i32 >> SA;
|
||||
}
|
||||
|
||||
auto RSP::instructionSRAV() -> void {
|
||||
RD.u32 = RT.i32 >> (RS.u32 & 31);
|
||||
}
|
||||
|
||||
auto RSP::instructionSRL() -> void {
|
||||
RD.u32 = i32(RT.u32 >> SA);
|
||||
}
|
||||
|
||||
auto RSP::instructionSRLV() -> void {
|
||||
RD.u32 = i32(RT.u32 >> (RS.u32 & 31));
|
||||
}
|
||||
|
||||
auto RSP::instructionSUBU() -> void {
|
||||
RD.u32 = i32(RS.u32 - RT.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionSW() -> void {
|
||||
dmem.writeWordUnaligned(RS.u32 + IMMi16, RT.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionXOR() -> void {
|
||||
RD.u32 = RS.u32 ^ RT.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionXORI() -> void {
|
||||
RT.u32 = RS.u32 ^ IMMu16;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//{
|
||||
union r32 {
|
||||
struct { int32_t i32; };
|
||||
struct { uint32_t u32; };
|
||||
};
|
||||
|
||||
struct Core {
|
||||
enum Register : uint {
|
||||
R0, AT, V0, V1, A0, A1, A2, A3,
|
||||
T0, T1, T2, T3, T4, T5, T6, T7,
|
||||
S0, S1, S2, S3, S4, S5, S6, S7,
|
||||
T8, T9, K0, K1, GP, SP, S8, RA,
|
||||
};
|
||||
|
||||
r32 r[32];
|
||||
u32 pc;
|
||||
} core;
|
||||
|
||||
struct Branch {
|
||||
enum : u32 { Step, Take, DelaySlot };
|
||||
|
||||
auto inDelaySlot() const -> bool { return state == DelaySlot; }
|
||||
auto reset() -> void { state = Step; }
|
||||
auto take(u32 address) -> void { state = Take; pc = address; }
|
||||
auto delaySlot() -> void { state = DelaySlot; }
|
||||
|
||||
u64 pc;
|
||||
u32 state;
|
||||
} branch;
|
||||
|
||||
//cpu-instructions.cpp
|
||||
auto instructionADDIU() -> void;
|
||||
auto instructionADDU() -> void;
|
||||
auto instructionAND() -> void;
|
||||
auto instructionANDI() -> void;
|
||||
auto instructionB(bool) -> void;
|
||||
auto instructionBAL(bool) -> void;
|
||||
auto instructionBREAK() -> void;
|
||||
auto instructionCACHE() -> void;
|
||||
auto instructionJ() -> void;
|
||||
auto instructionJAL() -> void;
|
||||
auto instructionJALR() -> void;
|
||||
auto instructionJR() -> void;
|
||||
auto instructionLB() -> void;
|
||||
auto instructionLBU() -> void;
|
||||
auto instructionLH() -> void;
|
||||
auto instructionLHU() -> void;
|
||||
auto instructionLUI() -> void;
|
||||
auto instructionLW() -> void;
|
||||
auto instructionNOR() -> void;
|
||||
auto instructionOR() -> void;
|
||||
auto instructionORI() -> void;
|
||||
auto instructionSB() -> void;
|
||||
auto instructionSH() -> void;
|
||||
auto instructionSLL() -> void;
|
||||
auto instructionSLLV() -> void;
|
||||
auto instructionSLT() -> void;
|
||||
auto instructionSLTI() -> void;
|
||||
auto instructionSLTIU() -> void;
|
||||
auto instructionSLTU() -> void;
|
||||
auto instructionSRA() -> void;
|
||||
auto instructionSRAV() -> void;
|
||||
auto instructionSRL() -> void;
|
||||
auto instructionSRLV() -> void;
|
||||
auto instructionSUBU() -> void;
|
||||
auto instructionSW() -> void;
|
||||
auto instructionXOR() -> void;
|
||||
auto instructionXORI() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,518 @@
|
||||
auto RSP::Disassembler::disassemble(u32 address, u32 instruction) -> string {
|
||||
this->address = address;
|
||||
this->instruction = instruction;
|
||||
|
||||
auto v = EXECUTE();
|
||||
if(!v) v.append("invalid", string{"$", hex(instruction, 8L)});
|
||||
if(!instruction) v = {"nop"};
|
||||
auto s = pad(v.takeFirst(), -8L);
|
||||
return {s, v.merge(",")};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::EXECUTE() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto imm16i = [&] { return immediate(i16(instruction)); };
|
||||
auto imm16u = [&] { return immediate(u16(instruction), 16L); };
|
||||
auto jump = [&] { return immediate(uint12(address + 4 & 0xf000'0000 | (instruction & 0x03ff'ffff) << 2)); };
|
||||
auto branch = [&] { return immediate(uint12(address + 4 + (i16(instruction) << 2))); };
|
||||
auto offset = [&] { return cpuRegisterIndex(instruction >> 21 & 31, i16(instruction)); };
|
||||
|
||||
auto ADDI = [&](string_view add, string_view sub, string_view mov) -> vector<string> {
|
||||
if(!(instruction >> 21 & 31)) return {mov, rtName(), immediate(i16(instruction), 32L)};
|
||||
return {i16(instruction) >= 0 ? add : sub, rtName(), rsValue(), immediate(abs(i16(instruction)))};
|
||||
};
|
||||
|
||||
auto ALU = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), rsValue(), immediate(u16(instruction))};
|
||||
};
|
||||
|
||||
auto BRANCH1 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
auto BRANCH2 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), rtValue(), branch()};
|
||||
};
|
||||
|
||||
auto CACHE = [&](string_view name) -> vector<string> {
|
||||
auto cache = instruction >> 16 & 3;
|
||||
auto op = instruction >> 18 & 7;
|
||||
string type = "reserved";
|
||||
if(cache == 0) switch(op) {
|
||||
case 0: type = "code(IndexInvalidate)"; break;
|
||||
case 1: type = "code(IndexLoadTag)"; break;
|
||||
case 2: type = "code(IndexStoreTag)"; break;
|
||||
case 4: type = "code(HitInvalidate)"; break;
|
||||
case 5: type = "code(Fill)"; break;
|
||||
case 6: type = "code(HitWriteBack)"; break;
|
||||
}
|
||||
if(cache == 1) switch(op) {
|
||||
case 0: type = "data(IndexWriteBackInvalidate)"; break;
|
||||
case 1: type = "data(IndexLoadTag)"; break;
|
||||
case 2: type = "data(IndexStoreTag)"; break;
|
||||
case 3: type = "data(CreateDirtyExclusive)"; break;
|
||||
case 4: type = "data(HitInvalidate)"; break;
|
||||
case 5: type = "data(HitWriteBackInvalidate)"; break;
|
||||
case 6: type = "data(HitWriteBack)"; break;
|
||||
}
|
||||
return {name, type, offset()};
|
||||
};
|
||||
|
||||
auto JUMP = [&](string_view name) -> vector<string> {
|
||||
return {name, jump()};
|
||||
};
|
||||
|
||||
auto LOAD = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), offset()};
|
||||
};
|
||||
|
||||
auto STORE = [&](string_view name) -> vector<string> {
|
||||
return {name, rtValue(), offset()};
|
||||
};
|
||||
|
||||
switch(instruction >> 26) {
|
||||
case 0x00: return SPECIAL();
|
||||
case 0x01: return REGIMM();
|
||||
case 0x02: return JUMP("j");
|
||||
case 0x03: return JUMP("jal");
|
||||
case 0x04: return BRANCH2("beq");
|
||||
case 0x05: return BRANCH2("bne");
|
||||
case 0x06: return BRANCH1("blez");
|
||||
case 0x07: return BRANCH1("bgtz");
|
||||
case 0x08: return ADDI("addi", "subi", "li");
|
||||
case 0x09: return ADDI("addiu", "subiu", "liu");
|
||||
case 0x0a: return ALU("slti");
|
||||
case 0x0b: return ALU("sltiu");
|
||||
case 0x0c: return ALU("andi");
|
||||
case 0x0d: return ALU("ori");
|
||||
case 0x0e: return ALU("xori");
|
||||
case 0x0f: return {"lui", rtName(), imm16u()};
|
||||
case 0x10: return SCC();
|
||||
case 0x11: break; //COP1
|
||||
case 0x12: return VU();
|
||||
case 0x13: break; //COP3
|
||||
case 0x14: break; //BEQL
|
||||
case 0x15: break; //BNEL
|
||||
case 0x16: break; //BLEZL
|
||||
case 0x17: break; //BGTZL
|
||||
case 0x18: break; //DADDI
|
||||
case 0x19: break; //DADDIU
|
||||
case 0x1a: break; //LDL
|
||||
case 0x1b: break; //LDR
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return LOAD("lb");
|
||||
case 0x21: return LOAD("lh");
|
||||
case 0x22: break; //LWL
|
||||
case 0x23: return LOAD("lw");
|
||||
case 0x24: return LOAD("lbu");
|
||||
case 0x25: return LOAD("lhu");
|
||||
case 0x26: break; //LWR
|
||||
case 0x27: break; //LWU
|
||||
case 0x28: return STORE("sb");
|
||||
case 0x29: return STORE("sh");
|
||||
case 0x2a: break; //SWL
|
||||
case 0x2b: return STORE("sw");
|
||||
case 0x2c: break; //SDL
|
||||
case 0x2d: break; //SDR
|
||||
case 0x2e: break; //SWR
|
||||
case 0x2f: return CACHE("cache");
|
||||
case 0x30: break; //LL
|
||||
case 0x31: break; //LWC1
|
||||
case 0x32: return LWC2();
|
||||
case 0x33: break; //LWC3
|
||||
case 0x34: break; //LLD
|
||||
case 0x35: break; //LDC1
|
||||
case 0x36: break; //LDC2
|
||||
case 0x37: break; //LD
|
||||
case 0x38: break; //SC
|
||||
case 0x39: break; //SWC1
|
||||
case 0x3a: return SWC2();
|
||||
case 0x3b: break; //SWC3
|
||||
case 0x3c: break; //SCD
|
||||
case 0x3d: break; //SDC1
|
||||
case 0x3e: break; //SDC2
|
||||
case 0x3f: break; //SD
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::SPECIAL() -> vector<string> {
|
||||
auto shift = [&] { return string{instruction >> 6 & 31}; };
|
||||
auto rdName = [&] { return cpuRegisterName (instruction >> 11 & 31); };
|
||||
auto rdValue = [&] { return cpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
|
||||
auto ALU = [&](string_view name, string_view by) -> vector<string> {
|
||||
return {name, rdName(), rtValue(), by};
|
||||
};
|
||||
|
||||
auto JALR = [&](string_view name) -> vector<string> {
|
||||
if((instruction >> 11 & 31) == 31) return {name, rsValue()};
|
||||
return {name, rdName(), rsValue()};
|
||||
};
|
||||
|
||||
auto REG = [&](string_view name) -> vector<string> {
|
||||
return {name, rdName(), rsValue(), rtValue()};
|
||||
};
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return ALU("sll", shift());
|
||||
case 0x01: break;
|
||||
case 0x02: return ALU("srl", shift());
|
||||
case 0x03: return ALU("sra", shift());
|
||||
case 0x04: return ALU("sllv", shift());
|
||||
case 0x05: break;
|
||||
case 0x06: return ALU("srlv", rsValue());
|
||||
case 0x07: return ALU("srav", rsValue());
|
||||
case 0x08: return {"jr", rsValue()};
|
||||
case 0x09: return JALR("jalr");
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: break; //SYSCALL
|
||||
case 0x0d: return {"break"};
|
||||
case 0x0e: break;
|
||||
case 0x0f: break; //SYNC
|
||||
case 0x10: break; //MFHI
|
||||
case 0x11: break; //MTHI
|
||||
case 0x12: break; //MFLO
|
||||
case 0x13: break; //MTLO
|
||||
case 0x14: break; //DSLLV
|
||||
case 0x15: break;
|
||||
case 0x16: break; //DSRLV
|
||||
case 0x17: break; //DSRAV
|
||||
case 0x18: break; //MULT
|
||||
case 0x19: break; //MULTU
|
||||
case 0x1a: break; //DIV
|
||||
case 0x1b: break; //DIVU
|
||||
case 0x1c: break; //DMULT
|
||||
case 0x1d: break; //DMULTU
|
||||
case 0x1e: break; //DDIV
|
||||
case 0x1f: break; //DDIVU
|
||||
case 0x20: return REG("add");
|
||||
case 0x21: return REG("addu");
|
||||
case 0x22: return REG("sub");
|
||||
case 0x23: return REG("subu");
|
||||
case 0x24: return REG("and");
|
||||
case 0x25: return REG("or");
|
||||
case 0x26: return REG("xor");
|
||||
case 0x27: return REG("nor");
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return REG("slt");
|
||||
case 0x2b: return REG("sltu");
|
||||
case 0x2c: break; //DADD
|
||||
case 0x2d: break; //DADDU
|
||||
case 0x2e: break; //DSUB
|
||||
case 0x2f: break; //DSUBU
|
||||
case 0x30: break; //TGE
|
||||
case 0x31: break; //TGEU
|
||||
case 0x32: break; //TLT
|
||||
case 0x33: break; //TLTU
|
||||
case 0x34: break; //TEQ
|
||||
case 0x35: break;
|
||||
case 0x36: break; //TNE
|
||||
case 0x37: break;
|
||||
case 0x38: break; //DSLL
|
||||
case 0x39: break;
|
||||
case 0x3a: break; //DSRL
|
||||
case 0x3b: break; //DSRA
|
||||
case 0x3c: break; //DSLL32
|
||||
case 0x3d: break;
|
||||
case 0x3e: break; //DSRL32
|
||||
case 0x3f: break; //DSRA32
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::REGIMM() -> vector<string> {
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto branch = [&] { return immediate(uint12(address + 4 + (i16(instruction) << 2))); };
|
||||
|
||||
auto BRANCH = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
switch(instruction >> 16 & 0x1f) {
|
||||
case 0x00: return BRANCH("bltz");
|
||||
case 0x01: return BRANCH("bgez");
|
||||
case 0x02: break; //BLTZL
|
||||
case 0x03: break; //BGEZL
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: break; //TGEI
|
||||
case 0x09: break; //TGEIU
|
||||
case 0x0a: break; //TLTI
|
||||
case 0x0b: break; //TLTIU
|
||||
case 0x0c: break; //TEQI
|
||||
case 0x0d: break;
|
||||
case 0x0e: break; //TNEI
|
||||
case 0x0f: break;
|
||||
case 0x10: return BRANCH("bltzal");
|
||||
case 0x11: return BRANCH("bgezal");
|
||||
case 0x12: break; //BLTZALL
|
||||
case 0x13: break; //BGEZALL
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::SCC() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto sdName = [&] { return sccRegisterName (instruction >> 11 & 31); };
|
||||
auto sdValue = [&] { return sccRegisterValue(instruction >> 11 & 31); };
|
||||
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
case 0x00: return {"mfc0", rtName(), sdValue()};
|
||||
case 0x04: return {"mtc0", sdName(), rtValue()};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::LWC2() -> vector<string> {
|
||||
auto vtName = [&] { return vuRegisterName (instruction >> 16 & 31, instruction >> 7 & 15); };
|
||||
auto vtValue = [&] { return vuRegisterValue (instruction >> 16 & 31, instruction >> 7 & 15); };
|
||||
auto offset = [&](uint multiplier) { return cpuRegisterIndex(instruction >> 21 & 31, int7(instruction) * multiplier); };
|
||||
|
||||
switch(instruction >> 11 & 31) {
|
||||
case 0x00: return {"lbv", vtName(), offset( 1)};
|
||||
case 0x01: return {"lsv", vtName(), offset( 2)};
|
||||
case 0x02: return {"llv", vtName(), offset( 4)};
|
||||
case 0x03: return {"ldv", vtName(), offset( 8)};
|
||||
case 0x04: return {"lqv", vtName(), offset(16)};
|
||||
case 0x05: return {"lrv", vtName(), offset(16)};
|
||||
case 0x06: return {"lpv", vtName(), offset( 8)};
|
||||
case 0x07: return {"luv", vtName(), offset( 8)};
|
||||
case 0x08: return {"lhv", vtName(), offset(16)};
|
||||
case 0x09: return {"lfv", vtName(), offset(16)};
|
||||
//case 0x0a: return {"lwv", vtName(), offset(16)}; //not present on N64 RSP
|
||||
case 0x0b: return {"ltv", vtName(), offset(16)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::SWC2() -> vector<string> {
|
||||
auto vtName = [&] { return vuRegisterName (instruction >> 16 & 31); };
|
||||
auto vtValue = [&] { return vuRegisterValue (instruction >> 16 & 31); };
|
||||
auto offset = [&](uint multiplier) { return cpuRegisterIndex(instruction >> 21 & 31, int7(instruction) * multiplier); };
|
||||
|
||||
switch(instruction >> 11 & 31) {
|
||||
case 0x00: return {"sbv", vtValue(), offset( 1)};
|
||||
case 0x01: return {"ssv", vtValue(), offset( 2)};
|
||||
case 0x02: return {"slv", vtValue(), offset( 4)};
|
||||
case 0x03: return {"sdv", vtValue(), offset( 8)};
|
||||
case 0x04: return {"sqv", vtValue(), offset(16)};
|
||||
case 0x05: return {"srv", vtValue(), offset(16)};
|
||||
case 0x06: return {"spv", vtValue(), offset( 8)};
|
||||
case 0x07: return {"suv", vtValue(), offset( 8)};
|
||||
case 0x08: return {"shv", vtValue(), offset(16)};
|
||||
case 0x09: return {"sfv", vtValue(), offset(16)};
|
||||
case 0x0a: return {"swv", vtValue(), offset(16)};
|
||||
case 0x0b: return {"stv", vtValue(), offset(16)};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::VU() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rdName = [&] { return vuRegisterName (instruction >> 11 & 31, instruction >> 7 & 15); };
|
||||
auto rdValue = [&] { return vuRegisterValue (instruction >> 11 & 31, instruction >> 7 & 15); };
|
||||
auto cdName = [&] { return ccrRegisterName (instruction >> 11 & 31); };
|
||||
auto cdValue = [&] { return ccrRegisterValue(instruction >> 11 & 31); };
|
||||
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
case 0x00: return {"mfc2", rtName(), rdValue()};
|
||||
case 0x02: return {"cfc2", rtName(), cdValue()};
|
||||
case 0x04: return {"mtc2", rtValue(), rdName()};
|
||||
case 0x06: return {"cfc2", rtValue(), cdName()};
|
||||
}
|
||||
if(!(instruction >> 25 & 1)) return {};
|
||||
|
||||
auto vdName = [&] { return vuRegisterName (instruction >> 6 & 31); };
|
||||
auto vdValue = [&] { return vuRegisterValue(instruction >> 6 & 31); };
|
||||
auto vsName = [&] { return vuRegisterName (instruction >> 11 & 31); };
|
||||
auto vsValue = [&] { return vuRegisterValue(instruction >> 11 & 31); };
|
||||
auto vtName = [&] { return vuRegisterName (instruction >> 16 & 31, instruction >> 21 & 15); };
|
||||
auto vtValue = [&] { return vuRegisterValue(instruction >> 16 & 31, instruction >> 21 & 15); };
|
||||
auto vmName = [&] { return vuRegisterName (instruction >> 6 & 31, instruction >> 11 & 31); };
|
||||
auto vmValue = [&] { return vuRegisterValue(instruction >> 6 & 31, instruction >> 11 & 31); };
|
||||
|
||||
auto DST = [&](string_view name) -> vector<string> {
|
||||
return {name, vdName(), vsValue(), vtValue()};
|
||||
};
|
||||
|
||||
auto DT = [&](string_view name) -> vector<string> {
|
||||
return {name, vmName(), vtValue()};
|
||||
};
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return DST("vmulf");
|
||||
case 0x01: return DST("vmulu");
|
||||
case 0x02: return DST("vrndp");
|
||||
case 0x03: return DST("vmulq");
|
||||
case 0x04: return DST("vmudl");
|
||||
case 0x05: return DST("vmudm");
|
||||
case 0x06: return DST("vmudn");
|
||||
case 0x07: return DST("vmudh");
|
||||
case 0x08: return DST("vmacf");
|
||||
case 0x09: return DST("vmacu");
|
||||
case 0x0a: return DST("vrndn");
|
||||
case 0x0b: return DST("vmacq");
|
||||
case 0x0c: return DST("vmadl");
|
||||
case 0x0d: return DST("vmadm");
|
||||
case 0x0e: return DST("vmadn");
|
||||
case 0x0f: return DST("vmadh");
|
||||
case 0x10: return DST("vadd");
|
||||
case 0x11: return DST("vsub");
|
||||
case 0x12: break;
|
||||
case 0x13: return DST("vabs");
|
||||
case 0x14: return DST("vaddc");
|
||||
case 0x15: return DST("vsubc");
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: return DST("vsar");
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return DST("vlt");
|
||||
case 0x21: return DST("veq");
|
||||
case 0x22: return DST("vne");
|
||||
case 0x23: return DST("vge");
|
||||
case 0x24: return DST("vcl");
|
||||
case 0x25: return DST("vch");
|
||||
case 0x26: return DST("vcr");
|
||||
case 0x27: return DST("vmrg");
|
||||
case 0x28: return DST("vand");
|
||||
case 0x29: return DST("vnand");
|
||||
case 0x2a: return DST("vor");
|
||||
case 0x2b: return DST("vnor");
|
||||
case 0x2c: return DST("vxor");
|
||||
case 0x2d: return DST("vnxor");
|
||||
case 0x2e: break;
|
||||
case 0x2f: break;
|
||||
case 0x30: return DT("vrcp");
|
||||
case 0x31: return DT("vrcpl");
|
||||
case 0x32: return DT("vrcph");
|
||||
case 0x33: return DT("vmov");
|
||||
case 0x34: return DT("vrsq");
|
||||
case 0x35: return DT("vrsql");
|
||||
case 0x36: return DT("vrsqh");
|
||||
case 0x37: return {"vnop"};
|
||||
case 0x38: break;
|
||||
case 0x39: break;
|
||||
case 0x3a: break;
|
||||
case 0x3b: break;
|
||||
case 0x3c: break;
|
||||
case 0x3d: break;
|
||||
case 0x3e: break;
|
||||
case 0x3f: break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::immediate(i64 value, uint bits) const -> string {
|
||||
if(value < 0) return {"-$", hex(-value, bits >> 2)};
|
||||
return {"$", hex(value, bits >> 2)};
|
||||
};
|
||||
|
||||
auto RSP::Disassembler::cpuRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
||||
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
||||
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::cpuRegisterValue(uint index) const -> string {
|
||||
if(index && showValues) return {cpuRegisterName(index), hint("{$", hex(self.core.r[index].u32, 8L), "}")};
|
||||
return cpuRegisterName(index);
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::cpuRegisterIndex(uint index, i16 offset) const -> string {
|
||||
string adjust;
|
||||
if(offset >= 0) adjust = {"+$", hex( offset)};
|
||||
if(offset < 0) adjust = {"-$", hex(-offset)};
|
||||
if(index && showValues) return {cpuRegisterName(index), adjust, hint("{$", hex(self.core.r[index].u32 + offset, 8L), "}")};
|
||||
return {cpuRegisterName(index), adjust};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::sccRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"SP_PBUS_ADDRESS", "SP_DRAM_ADDRESS", "SP_READ_LENGTH", "SP_WRITE_LENGTH",
|
||||
"SP_STATUS", "SP_DMA_FULL", "SP_DMA_BUSY", "SP_SEMAPHORE",
|
||||
"DPC_START", "DPC_END", "DPC_CURRENT", "DPC_STATUS",
|
||||
"DPC_CLOCK", "DPC_BUSY", "DPC_PIPE_BUSY", "DPC_TMEM_BUSY",
|
||||
};
|
||||
return registers[index & 15];
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::sccRegisterValue(uint index) const -> string {
|
||||
u32 value = 0;
|
||||
if(index <= 6) value = rsp.readSCC((index & 7) << 2);
|
||||
if(index == 7) value = self.status.semaphore; //rsp.readSCC(7) has side-effects
|
||||
if(index >= 8) value = rdp.readSCC((index & 7) << 2);
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(value, 8L), "}")};
|
||||
return sccRegisterName(index);
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::vuRegisterName(uint index, uint element) const -> string {
|
||||
if(element) return {"v", index, "[", element, "]"};
|
||||
return {"v", index};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::vuRegisterValue(uint index, uint element) const -> string {
|
||||
if(showValues) {
|
||||
vector<string> elements;
|
||||
for(uint index : range(8)) elements.append(hex(self.vu.r[index].element(index), 4L));
|
||||
return {vuRegisterName(index, element), hint("{$", elements.merge("|"), "}")};
|
||||
}
|
||||
return vuRegisterName(index, element);
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::ccrRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {"vco", "vcc", "vce"};
|
||||
if(index < 3) return registers[index];
|
||||
return {"vc", index};
|
||||
}
|
||||
|
||||
auto RSP::Disassembler::ccrRegisterValue(uint index) const -> string {
|
||||
if(showValues) return {ccrRegisterName(index)}; //todo
|
||||
return ccrRegisterName(index);
|
||||
}
|
||||
|
||||
template<typename... P>
|
||||
auto RSP::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//{
|
||||
struct Disassembler {
|
||||
RSP& self;
|
||||
Disassembler(RSP& self) : self(self) {}
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(u32 address, u32 instruction) -> string;
|
||||
template<typename... P> auto hint(P&&... p) const -> string;
|
||||
|
||||
bool showColors = true;
|
||||
bool showValues = true;
|
||||
|
||||
private:
|
||||
auto EXECUTE() -> vector<string>;
|
||||
auto SPECIAL() -> vector<string>;
|
||||
auto REGIMM() -> vector<string>;
|
||||
auto SCC() -> vector<string>;
|
||||
auto LWC2() -> vector<string>;
|
||||
auto SWC2() -> vector<string>;
|
||||
auto VU() -> vector<string>;
|
||||
auto immediate(i64 value, uint bits = 0) const -> string;
|
||||
auto cpuRegisterName(uint index) const -> string;
|
||||
auto cpuRegisterValue(uint index) const -> string;
|
||||
auto cpuRegisterIndex(uint index, i16 offset) const -> string;
|
||||
auto sccRegisterName(uint index) const -> string;
|
||||
auto sccRegisterValue(uint index) const -> string;
|
||||
auto vuRegisterName(uint index, uint element = 0) const -> string;
|
||||
auto vuRegisterValue(uint index, uint element = 0) const -> string;
|
||||
auto ccrRegisterName(uint index) const -> string;
|
||||
auto ccrRegisterValue(uint index) const -> string;
|
||||
|
||||
u32 address;
|
||||
u32 instruction;
|
||||
} disassembler{*this};
|
||||
//};
|
||||
@@ -0,0 +1,291 @@
|
||||
auto RSP::instruction() -> void {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = imem.readWord(pipeline.address);
|
||||
|
||||
debugger.instruction();
|
||||
//instructionDEBUG();
|
||||
instructionEXECUTE();
|
||||
GPR[0].u32 = 0;
|
||||
|
||||
switch(branch.state) {
|
||||
case Branch::Step: PC += 4; break;
|
||||
case Branch::DelaySlot: PC = branch.pc; branch.reset(); break;
|
||||
case Branch::Take: PC += 4; branch.delaySlot(); break;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionDEBUG() -> void {
|
||||
static uint counter = 0;
|
||||
//if(++counter > 100) return;
|
||||
print(
|
||||
disassembler.hint(hex(pipeline.address, 3L)), " ",
|
||||
//disassembler.hint(hex(pipeline.instruction, 8L)), " ",
|
||||
disassembler.disassemble(pipeline.address, pipeline.instruction), "\n"
|
||||
);
|
||||
}
|
||||
|
||||
auto RSP::instructionEXECUTE() -> void {
|
||||
switch(OP >> 26) {
|
||||
case 0x00: return instructionSPECIAL();
|
||||
case 0x01: return instructionREGIMM();
|
||||
case 0x02: return instructionJ();
|
||||
case 0x03: return instructionJAL();
|
||||
case 0x04: return instructionB(RS.u32 == RT.u32); //BEQ
|
||||
case 0x05: return instructionB(RS.u32 != RT.u32); //BNE
|
||||
case 0x06: return instructionB(RS.i32 <= 0); //BLEZ
|
||||
case 0x07: return instructionB(RS.i32 > 0); //BGTZ
|
||||
case 0x08: return instructionADDIU(); //ADDI
|
||||
case 0x09: return instructionADDIU();
|
||||
case 0x0a: return instructionSLTI();
|
||||
case 0x0b: return instructionSLTIU();
|
||||
case 0x0c: return instructionANDI();
|
||||
case 0x0d: return instructionORI();
|
||||
case 0x0e: return instructionXORI();
|
||||
case 0x0f: return instructionLUI();
|
||||
case 0x10: return instructionCOP0();
|
||||
case 0x11: break; //COP1
|
||||
case 0x12: return instructionCOP2();
|
||||
case 0x13: break; //COP3
|
||||
case 0x14: break; //BEQL
|
||||
case 0x15: break; //BNEL
|
||||
case 0x16: break; //BLEZL
|
||||
case 0x17: break; //BGTZL
|
||||
case 0x18: break; //DADDI
|
||||
case 0x19: break; //DADDIU
|
||||
case 0x1a: break; //LDL
|
||||
case 0x1b: break; //LDR
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return instructionLB();
|
||||
case 0x21: return instructionLH();
|
||||
case 0x22: break; //LWL
|
||||
case 0x23: return instructionLW();
|
||||
case 0x24: return instructionLBU();
|
||||
case 0x25: return instructionLHU();
|
||||
case 0x26: break; //LWR
|
||||
case 0x27: break; //LWU
|
||||
case 0x28: return instructionSB();
|
||||
case 0x29: return instructionSH();
|
||||
case 0x2a: break; //SWL
|
||||
case 0x2b: return instructionSW();
|
||||
case 0x2c: break; //SDL
|
||||
case 0x2d: break; //SDR
|
||||
case 0x2e: break; //SWR
|
||||
case 0x2f: return instructionCACHE();
|
||||
case 0x30: break; //LL
|
||||
case 0x31: break; //LWC1
|
||||
case 0x32: return instructionLWC2();
|
||||
case 0x33: break; //LWC3
|
||||
case 0x34: break; //LLD
|
||||
case 0x35: break; //LDC1
|
||||
case 0x36: break; //LDC2
|
||||
case 0x37: break; //LD
|
||||
case 0x38: break; //SC
|
||||
case 0x39: break; //SWC1
|
||||
case 0x3a: return instructionSWC2();
|
||||
case 0x3b: break; //SWC3
|
||||
case 0x3c: break; //SCD
|
||||
case 0x3d: break; //SDC1
|
||||
case 0x3e: break; //SDC2
|
||||
case 0x3f: break; //SD
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSPECIAL() -> void {
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionSLL();
|
||||
case 0x01: break;
|
||||
case 0x02: return instructionSRL();
|
||||
case 0x03: return instructionSRA();
|
||||
case 0x04: return instructionSLLV();
|
||||
case 0x05: break;
|
||||
case 0x06: return instructionSRLV();
|
||||
case 0x07: return instructionSRAV();
|
||||
case 0x08: return instructionJR();
|
||||
case 0x09: return instructionJALR();
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: break; //SYSCALL
|
||||
case 0x0d: return instructionBREAK();
|
||||
case 0x0e: break;
|
||||
case 0x0f: break; //SYNC
|
||||
case 0x10: break; //MFHI
|
||||
case 0x11: break; //MTHI
|
||||
case 0x12: break; //MFLO
|
||||
case 0x13: break; //MTLO
|
||||
case 0x14: break; //DSLLV
|
||||
case 0x15: break;
|
||||
case 0x16: break; //DSRLV
|
||||
case 0x17: break; //DSRAV
|
||||
case 0x18: break; //MULT
|
||||
case 0x19: break; //MULTU
|
||||
case 0x1a: break; //DIV
|
||||
case 0x1b: break; //DIVU
|
||||
case 0x1c: break; //DMULT
|
||||
case 0x1d: break; //DMULTU
|
||||
case 0x1e: break; //DDIV
|
||||
case 0x1f: break; //DDIVU
|
||||
case 0x20: return instructionADDU(); //ADD
|
||||
case 0x21: return instructionADDU();
|
||||
case 0x22: return instructionSUBU(); //SUB
|
||||
case 0x23: return instructionSUBU();
|
||||
case 0x24: return instructionAND();
|
||||
case 0x25: return instructionOR();
|
||||
case 0x26: return instructionXOR();
|
||||
case 0x27: return instructionNOR();
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return instructionSLT();
|
||||
case 0x2b: return instructionSLTU();
|
||||
case 0x2c: break; //DADD
|
||||
case 0x2d: break; //DADDU
|
||||
case 0x2e: break; //DSUB
|
||||
case 0x2f: break; //DSUBU
|
||||
case 0x30: break; //TGE
|
||||
case 0x31: break; //TGEU
|
||||
case 0x32: break; //TLT
|
||||
case 0x33: break; //TLTU
|
||||
case 0x34: break; //TEQ
|
||||
case 0x35: break;
|
||||
case 0x36: break; //TNE
|
||||
case 0x37: break;
|
||||
case 0x38: break; //DSLL
|
||||
case 0x39: break;
|
||||
case 0x3a: break; //DSRL
|
||||
case 0x3b: break; //DSRA
|
||||
case 0x3c: break; //DSLL32
|
||||
case 0x3d: break;
|
||||
case 0x3e: break; //DSRL32
|
||||
case 0x3f: break; //DSRA32
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionREGIMM() -> void {
|
||||
switch(OP >> 16 & 0x1f) {
|
||||
case 0x00: return instructionB(RS.i32 < 0); //BLTZ
|
||||
case 0x01: return instructionB(RS.i32 >= 0); //BGEZ
|
||||
case 0x02: break; //BLTZL
|
||||
case 0x03: break; //BGEZL
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: break; //TGEI
|
||||
case 0x09: break; //TGEIU
|
||||
case 0x0a: break; //TLTI
|
||||
case 0x0b: break; //TLTIU
|
||||
case 0x0c: break; //TEQI
|
||||
case 0x0d: break;
|
||||
case 0x0e: break; //TNEI
|
||||
case 0x0f: break;
|
||||
case 0x10: return instructionBAL(RS.i32 < 0); //BLTZAL
|
||||
case 0x11: return instructionBAL(RS.i32 >= 0); //BGEZAL
|
||||
case 0x12: break; //BLTZALL
|
||||
case 0x13: break; //BGEZALL
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionCOP0() -> void {
|
||||
switch(OP >> 21 & 31) {
|
||||
case 0x00: return instructionMFC0();
|
||||
case 0x01: break; //DMFC0
|
||||
case 0x02: break; //CFC0
|
||||
case 0x04: return instructionMTC0();
|
||||
case 0x05: break; //DMTC0
|
||||
case 0x06: break; //CTC0
|
||||
case 0x08: break; //BC0
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionCOP2() -> void {
|
||||
switch(OP >> 21 & 31) {
|
||||
case 0x00: return instructionMFC2();
|
||||
case 0x01: break; //DMFC2
|
||||
case 0x02: return instructionCFC2();
|
||||
case 0x04: return instructionMTC2();
|
||||
case 0x05: break; //DMTC2
|
||||
case 0x06: return instructionCTC2();
|
||||
case 0x08: break; //BC2
|
||||
}
|
||||
if(!(OP >> 25 & 1)) return;
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionVMULF(0);
|
||||
case 0x01: return instructionVMULF(1);
|
||||
case 0x02: return instructionVRND(1);
|
||||
case 0x03: return instructionVMULQ();
|
||||
case 0x04: return instructionVMUDL();
|
||||
case 0x05: return instructionVMUDM();
|
||||
case 0x06: return instructionVMUDN();
|
||||
case 0x07: return instructionVMUDH();
|
||||
case 0x08: return instructionVMACF(0);
|
||||
case 0x09: return instructionVMACF(1);
|
||||
case 0x0a: return instructionVRND(0);
|
||||
case 0x0b: return instructionVMACQ();
|
||||
case 0x0c: return instructionVMADL();
|
||||
case 0x0d: return instructionVMADM();
|
||||
case 0x0e: return instructionVMADN();
|
||||
case 0x0f: return instructionVMADH();
|
||||
case 0x10: return instructionVADD();
|
||||
case 0x11: return instructionVSUB();
|
||||
case 0x12: break;
|
||||
case 0x13: return instructionVABS();
|
||||
case 0x14: return instructionVADDC();
|
||||
case 0x15: return instructionVSUBC();
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: return instructionVSAR();
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return instructionVLT();
|
||||
case 0x21: return instructionVEQ();
|
||||
case 0x22: return instructionVNE();
|
||||
case 0x23: return instructionVGE();
|
||||
case 0x24: return instructionVCL();
|
||||
case 0x25: return instructionVCH();
|
||||
case 0x26: return instructionVCR();
|
||||
case 0x27: return instructionVMRG();
|
||||
case 0x28: return instructionVAND();
|
||||
case 0x29: return instructionVNAND();
|
||||
case 0x2a: return instructionVOR();
|
||||
case 0x2b: return instructionVNOR();
|
||||
case 0x2c: return instructionVXOR();
|
||||
case 0x2d: return instructionVNXOR();
|
||||
case 0x2e: break;
|
||||
case 0x2f: break;
|
||||
case 0x30: return instructionVRCP(0);
|
||||
case 0x31: return instructionVRCP(1);
|
||||
case 0x32: return instructionVRCPH();
|
||||
case 0x33: return instructionVMOV();
|
||||
case 0x34: return instructionVRSQ(0);
|
||||
case 0x35: return instructionVRSQ(1);
|
||||
case 0x36: return instructionVRSQH();
|
||||
case 0x37: return instructionVNOP();
|
||||
case 0x38: break;
|
||||
case 0x39: break;
|
||||
case 0x3a: break;
|
||||
case 0x3b: break;
|
||||
case 0x3c: break;
|
||||
case 0x3d: break;
|
||||
case 0x3e: break;
|
||||
case 0x3f: break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
//{
|
||||
struct Pipeline {
|
||||
u32 address;
|
||||
u32 instruction;
|
||||
} pipeline;
|
||||
//};
|
||||
@@ -0,0 +1,95 @@
|
||||
#if !defined(RSP_REGISTERS_HPP)
|
||||
#define RSP_REGISTERS_HPP
|
||||
|
||||
//instruction decoding
|
||||
#define OP pipeline.instruction
|
||||
#define SA (OP >> 6 & 31)
|
||||
#define RDn (OP >> 11 & 31)
|
||||
#define RTn (OP >> 16 & 31)
|
||||
#define RSn (OP >> 21 & 31)
|
||||
#define IMMi16 i16(OP)
|
||||
#define IMMu16 u16(OP)
|
||||
#define IMMu26 (OP & 0x03ff'ffff)
|
||||
|
||||
//CPU
|
||||
#define GPR core.r
|
||||
#define PC core.pc
|
||||
|
||||
#define RD GPR[RDn]
|
||||
#define RT GPR[RTn]
|
||||
#define RS GPR[RSn]
|
||||
#define RA GPR[Core::Register::RA]
|
||||
|
||||
//VU
|
||||
#define VDn (OP >> 6 & 31)
|
||||
#define VSn (OP >> 11 & 31)
|
||||
#define VTn (OP >> 16 & 31)
|
||||
#define DEn (OP >> 11 & 31)
|
||||
#define En (OP >> 21 & 15)
|
||||
|
||||
#define VPR vu.r
|
||||
#define VD VPR[VDn]
|
||||
#define VS VPR[VSn]
|
||||
#define VT VPR[VTn]
|
||||
#define VTe this->vte()
|
||||
|
||||
#define ACCH vu.acch
|
||||
#define ACCM vu.accm
|
||||
#define ACCL vu.accl
|
||||
#define VCOH vu.vcoh
|
||||
#define VCOL vu.vcol
|
||||
#define VCCH vu.vcch
|
||||
#define VCCL vu.vccl
|
||||
#define VCE vu.vce
|
||||
|
||||
#define DIVIN vu.divin
|
||||
#define DIVOUT vu.divout
|
||||
#define DIVDP vu.divdp
|
||||
#else
|
||||
#undef RSP_REGISTERS_HPP
|
||||
|
||||
//instruction decoding
|
||||
#undef OP
|
||||
#undef SA
|
||||
#undef RDn
|
||||
#undef RTn
|
||||
#undef RSn
|
||||
#undef IMMi16
|
||||
#undef IMMu16
|
||||
#undef IMMu26
|
||||
|
||||
//CPU
|
||||
#undef GPR
|
||||
#undef PC
|
||||
|
||||
#undef RD
|
||||
#undef RT
|
||||
#undef RS
|
||||
#undef RA
|
||||
|
||||
//VU
|
||||
#undef VDn
|
||||
#undef VSn
|
||||
#undef VTn
|
||||
#undef DEn
|
||||
#undef En
|
||||
|
||||
#undef VPR
|
||||
#undef VD
|
||||
#undef VS
|
||||
#undef VT
|
||||
#undef VTe
|
||||
|
||||
#undef ACCH
|
||||
#undef ACCM
|
||||
#undef ACCL
|
||||
#undef VCOH
|
||||
#undef VCOL
|
||||
#undef VCCH
|
||||
#undef VCCL
|
||||
#undef VCE
|
||||
|
||||
#undef DIVIN
|
||||
#undef DIVOUT
|
||||
#undef DIVDP
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
auto RSP::instructionMFC0() -> void {
|
||||
if((RDn & 8) == 0) RT.u32 = rsp.readSCC((RDn & 7) << 2);
|
||||
if((RDn & 8) != 0) RT.u32 = rdp.readSCC((RDn & 7) << 2);
|
||||
}
|
||||
|
||||
auto RSP::instructionMTC0() -> void {
|
||||
if((RDn & 8) == 0) rsp.writeSCC((RDn & 7) << 2, RT.u32);
|
||||
if((RDn & 8) != 0) rdp.writeSCC((RDn & 7) << 2, RT.u32);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//{
|
||||
//scc-registers.cpp
|
||||
auto getControlRegister(uint) -> u32;
|
||||
auto setControlRegister(uint, uint32) -> void;
|
||||
|
||||
//scc-instructions.cpp
|
||||
auto instructionMFC0() -> void;
|
||||
auto instructionMTC0() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,2 @@
|
||||
auto RSP::serializeCore(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,949 @@
|
||||
//returns VT[e]
|
||||
auto RSP::vte() -> v128 {
|
||||
//SSE byte indexes are in little-endian ordering; read from right to left
|
||||
static const __m128i shuffle[16] = {
|
||||
//vector
|
||||
_mm_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15), //01234567
|
||||
_mm_setr_epi8( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15), //01234567
|
||||
//scalar quarter
|
||||
_mm_setr_epi8( 2, 3, 2, 3, 6, 7, 6, 7,10,11,10,11,14,15,14,15), //00224466
|
||||
_mm_setr_epi8( 0, 1, 0, 1, 4, 5, 4, 5, 8, 9, 8, 9,12,13,12,13), //11335577
|
||||
//scalar half
|
||||
_mm_setr_epi8( 6, 7, 6, 7, 6, 7, 6, 7,14,15,14,15,14,15,14,15), //00004444
|
||||
_mm_setr_epi8( 4, 5, 4, 5, 4, 5, 4, 5,12,13,12,13,12,13,12,13), //11115555
|
||||
_mm_setr_epi8( 2, 3, 2, 3, 2, 3, 2, 3,10,11,10,11,10,11,10,11), //22226666
|
||||
_mm_setr_epi8( 0, 1, 0, 1, 0, 1, 0, 1, 8, 9, 8, 9, 8, 9, 8, 9), //33337777
|
||||
//scalar whole
|
||||
_mm_setr_epi8(14,15,14,15,14,15,14,15,14,15,14,15,14,15,14,15), //00000000
|
||||
_mm_setr_epi8(12,13,12,13,12,13,12,13,12,13,12,13,12,13,12,13), //11111111
|
||||
_mm_setr_epi8(10,11,10,11,10,11,10,11,10,11,10,11,10,11,10,11), //22222222
|
||||
_mm_setr_epi8( 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9), //33333333
|
||||
_mm_setr_epi8( 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7), //44444444
|
||||
_mm_setr_epi8( 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5), //55555555
|
||||
_mm_setr_epi8( 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3), //66666666
|
||||
_mm_setr_epi8( 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1), //77777777
|
||||
};
|
||||
//todo: benchmark to see if testing for cases 0&1 to return value directly is faster
|
||||
return {u128(_mm_shuffle_epi8(VT.s128, shuffle[OP >> 21 & 15]))};
|
||||
}
|
||||
|
||||
auto RSP::instructionCFC2() -> void {
|
||||
v128 hi, lo;
|
||||
switch(RDn & 3) {
|
||||
case 0x00: hi = VCOH; lo = VCOL; break;
|
||||
case 0x01: hi = VCCH; lo = VCCL; break;
|
||||
case 0x02: hi = zero; lo = VCE; break;
|
||||
case 0x03: hi = zero; lo = VCE; break; //unverified
|
||||
}
|
||||
|
||||
u16 value = _mm_movemask_epi8(_mm_packs_epi16(hi, lo));
|
||||
value = (value & 0xff00) >> 8 | (value & 0x00ff) << 8;
|
||||
value = (value & 0xf0f0) >> 4 | (value & 0x0f0f) << 4;
|
||||
value = (value & 0xcccc) >> 2 | (value & 0x3333) << 2;
|
||||
value = (value & 0xaaaa) >> 1 | (value & 0x5555) << 1;
|
||||
RT.u32 = i16(value);
|
||||
}
|
||||
|
||||
auto RSP::instructionCTC2() -> void {
|
||||
maybe<v128&> hi, lo;
|
||||
v128 zero;
|
||||
switch(RDn & 3) {
|
||||
case 0x00: hi = VCOH; lo = VCOL; break;
|
||||
case 0x01: hi = VCCH; lo = VCCL; break;
|
||||
case 0x02: hi = zero; lo = VCE; break;
|
||||
case 0x03: hi = zero; lo = VCE; break; //unverified
|
||||
}
|
||||
|
||||
#if 1
|
||||
u16 rt = RT.u32;
|
||||
lo->s128 = _mm_set_epi16(
|
||||
0 - (rt >> 0 & 1), 0 - (rt >> 1 & 1), 0 - (rt >> 2 & 1), 0 - (rt >> 3 & 1),
|
||||
0 - (rt >> 4 & 1), 0 - (rt >> 5 & 1), 0 - (rt >> 6 & 1), 0 - (rt >> 7 & 1)
|
||||
);
|
||||
hi->s128 = _mm_set_epi16(
|
||||
0 - (rt >> 8 & 1), 0 - (rt >> 9 & 1), 0 - (rt >> 10 & 1), 0 - (rt >> 11 & 1),
|
||||
0 - (rt >> 12 & 1), 0 - (rt >> 13 & 1), 0 - (rt >> 14 & 1), 0 - (rt >> 15 & 1)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
u16 rt = RT.u32;
|
||||
for(uint bit : range(8)) {
|
||||
hi->element(bit) = 0 - bool(rt & 0x0100 << bit);
|
||||
lo->element(bit) = 0 - bool(rt & 0x0001 << bit);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RSP::instructionLBV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP);
|
||||
auto index = OP >> 7 & 15;
|
||||
data.byte(index) = dmem.readByte(address);
|
||||
}
|
||||
|
||||
auto RSP::instructionLDV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 8;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 8;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
data.byte(offset & 15) = dmem.readByte(address++);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLFV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
auto start = index >> 1;
|
||||
auto end = start + 4;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
data.element(offset & 7) = dmem.readByte(address) << 7;
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLHV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
for(uint offset = 0; offset < 8; offset++) {
|
||||
data.element(offset) = dmem.readByte(address + (16 - index + offset * 2 & 15)) << 7;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLLV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 4;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 4;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
data.byte(offset & 15) = dmem.readByte(address++);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLPV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 8;
|
||||
auto index = OP >> 7 & 15;
|
||||
for(uint offset = 0; offset < 8; offset++) {
|
||||
data.element(offset) = dmem.readByte(address + (16 - index + offset & 15)) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLQV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto start = 0; //OP >> 7 & 15; ?
|
||||
auto end = 16 - (address & 15);
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
data.byte(offset & 15) = dmem.readByte(address++);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLRV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15; //0?
|
||||
auto start = 16 - ((address & 15) - index);
|
||||
address &= ~15;
|
||||
for(uint offset = start; offset < 16; offset++) {
|
||||
data.byte(offset & 15) = dmem.readByte(address++);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLSV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 2;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 2;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
data.byte(offset & 15) = dmem.readByte(address++);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLTV() -> void {
|
||||
#if 1
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
auto start = OP >> 16 & 31;
|
||||
auto end = min(32, start + 8);
|
||||
address = (address + 8 & ~15) + (index & 1);
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
auto byte = (8 - (index >> 1) + (offset - start)) << 1;
|
||||
VPR[offset].byte(byte + 0 & 15) = dmem.readByte(address++);
|
||||
VPR[offset].byte(byte + 1 & 15) = dmem.readByte(address++);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
u32 address = RS.u32 + int7(OP) * 16;
|
||||
u32 element = OP >> 8 & 7;
|
||||
u32 vt = VTn & ~7;
|
||||
v128 vs{dmem.readQuadUnaligned(address)};
|
||||
for(u32 index : range(8)) {
|
||||
VPR[vt++].element(index - element & 7) = vs.element(index);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RSP::instructionLUV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 8;
|
||||
auto index = OP >> 7 & 15;
|
||||
for(uint offset = 0; offset < 8; offset++) {
|
||||
data.element(offset) = dmem.readByte(address + (16 - index + offset & 15)) << 7;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLWC2() -> void {
|
||||
switch(OP >> 11 & 31) {
|
||||
case 0x00: return instructionLBV();
|
||||
case 0x01: return instructionLSV();
|
||||
case 0x02: return instructionLLV();
|
||||
case 0x03: return instructionLDV();
|
||||
case 0x04: return instructionLQV();
|
||||
case 0x05: return instructionLRV();
|
||||
case 0x06: return instructionLPV();
|
||||
case 0x07: return instructionLUV();
|
||||
case 0x08: return instructionLHV();
|
||||
case 0x09: return instructionLFV();
|
||||
//case 0x0a: return instructionLWV(); //not present on N64 RSP
|
||||
case 0x0b: return instructionLTV();
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionLWV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
auto start = 16 - index;
|
||||
auto end = index + 16;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
data.byte(offset & 15) = dmem.readByte(address);
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionMFC2() -> void {
|
||||
auto index = OP >> 7 & 15;
|
||||
auto hi = VS.byte(index + 0 & 15);
|
||||
auto lo = VS.byte(index + 1 & 15);
|
||||
RT.u32 = i16(hi << 8 | lo << 0);
|
||||
}
|
||||
|
||||
auto RSP::instructionMTC2() -> void {
|
||||
auto index = OP >> 7 & 15;
|
||||
VS.byte(index + 0 & 15) = RT.u32 >> 8;
|
||||
VS.byte(index + 1 & 15) = RT.u32 >> 0;
|
||||
}
|
||||
|
||||
auto RSP::instructionSBV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP);
|
||||
auto index = OP >> 7 & 15;
|
||||
dmem.writeByte(address, data.byte(index));
|
||||
}
|
||||
|
||||
auto RSP::instructionSDV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 8;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 8;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address++, data.byte(offset & 15));
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSFV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
auto start = index >> 1;
|
||||
auto end = start + 4;
|
||||
auto base = address & 15;
|
||||
address &= ~15;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address + (base & 15), data.element(offset & 7) >> 7);
|
||||
base += 4;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSHV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
for(uint offset = 0; offset < 8; offset++) {
|
||||
auto byte = index + offset * 2;
|
||||
auto value = data.byte(byte + 0 & 15) << 1 | data.byte(byte + 1 & 15) >> 7;
|
||||
dmem.writeByte(address, value);
|
||||
address += 2;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSLV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 4;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 4;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address++, data.byte(offset & 15));
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSPV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 8;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 8;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
if((offset & 15) < 8) {
|
||||
dmem.writeByte(address++, data.byte((offset & 7) << 1));
|
||||
} else {
|
||||
dmem.writeByte(address++, data.element(offset & 7) >> 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSQV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + (16 - (address & 15));
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address++, data.byte(offset & 15));
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSRV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + (address & 15);
|
||||
auto base = 16 - (address & 15);
|
||||
address &= ~15;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address++, data.byte(offset + base & 15));
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSSV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 2;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 2;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address++, data.byte(offset & 15));
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSTV() -> void {
|
||||
#if 1
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto index = OP >> 7 & 15;
|
||||
auto start = OP >> 16 & 31;
|
||||
auto end = min(32, start + 8);
|
||||
auto element = 8 - (index >> 1);
|
||||
auto base = (address & 15) + (element << 1);
|
||||
address &= ~15;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeHalfUnaligned(address + (base & 15), VPR[offset].element(element++ & 7));
|
||||
base += 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
u32 address = RS.u32 + int7(OP) * 16;
|
||||
u32 element = OP >> 8 & 7;
|
||||
u32 vt = VTn & ~7;
|
||||
v128 vs;
|
||||
for(u32 index : range(8)) {
|
||||
vs.element(index - element & 7) = VPR[vt++].element(index - element & 7);
|
||||
}
|
||||
dmem.writeQuadUnaligned(address, vs.u128);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RSP::instructionSUV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 8;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 8;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
if((offset & 15) < 8) {
|
||||
dmem.writeByte(address++, data.element(offset & 7) >> 7);
|
||||
} else {
|
||||
dmem.writeByte(address++, data.byte((offset & 7) << 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSWC2() -> void {
|
||||
switch(OP >> 11 & 31) {
|
||||
case 0x00: return instructionSBV();
|
||||
case 0x01: return instructionSSV();
|
||||
case 0x02: return instructionSLV();
|
||||
case 0x03: return instructionSDV();
|
||||
case 0x04: return instructionSQV();
|
||||
case 0x05: return instructionSRV();
|
||||
case 0x06: return instructionSPV();
|
||||
case 0x07: return instructionSUV();
|
||||
case 0x08: return instructionSHV();
|
||||
case 0x09: return instructionSFV();
|
||||
case 0x0a: return instructionSWV();
|
||||
case 0x0b: return instructionSTV();
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSWV() -> void {
|
||||
auto& data = VT;
|
||||
auto address = RS.u32 + int7(OP) * 16;
|
||||
auto start = OP >> 7 & 15;
|
||||
auto end = start + 16;
|
||||
auto base = address & 15;
|
||||
address &= ~15;
|
||||
for(uint offset = start; offset < end; offset++) {
|
||||
dmem.writeByte(address + (base++ & 15), data.byte(offset & 15));
|
||||
}
|
||||
|
||||
#if 0
|
||||
u32 address = RS.u32 + int7(OP) * 16;
|
||||
u32 rotate = (OP >> 8 & 7) * 16;
|
||||
v128 vt{VPR[VTn].u128};
|
||||
v128 vs{vt.u128 << rotate | vt.u128 >> 128 - rotate};
|
||||
dmem.writeQuadUnaligned(address, vs.u128);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RSP::instructionVABS() -> void {
|
||||
v128 vte = VTe, vs0, slt;
|
||||
vs0 = _mm_cmpeq_epi16(VS, zero);
|
||||
slt = _mm_srai_epi16(VS, 15);
|
||||
VD = _mm_andnot_si128(vs0, vte);
|
||||
VD = _mm_xor_si128(VD, slt);
|
||||
ACCL = _mm_sub_epi16(VD, slt);
|
||||
VD = _mm_subs_epi16(VD, slt);
|
||||
}
|
||||
|
||||
auto RSP::instructionVADD() -> void {
|
||||
v128 vte = VTe, sum, min, max;
|
||||
sum = _mm_add_epi16(VS, vte);
|
||||
ACCL = _mm_sub_epi16(sum, VCOL);
|
||||
min = _mm_min_epi16(VS, vte);
|
||||
max = _mm_max_epi16(VS, vte);
|
||||
min = _mm_subs_epi16(min, VCOL);
|
||||
VD = _mm_adds_epi16(min, max);
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
}
|
||||
|
||||
auto RSP::instructionVADDC() -> void {
|
||||
v128 vte = VTe, sum;
|
||||
sum = _mm_adds_epu16(VS, vte);
|
||||
ACCL = _mm_add_epi16(VS, vte);
|
||||
VCOL = _mm_cmpeq_epi16(sum, ACCL);
|
||||
VCOL = _mm_cmpeq_epi16(VCOL, zero);
|
||||
VCOH = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVAND() -> void {
|
||||
ACCL = _mm_and_si128(VS, VTe);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVCL() -> void {
|
||||
v128 vte = VTe, nvt, diff, ncarry, nvce, diff0, lec1, lec2, leeq, geeq, le, ge, mask;
|
||||
nvt = _mm_xor_si128(vte, VCOL);
|
||||
nvt = _mm_sub_epi16(nvt, VCOL);
|
||||
diff = _mm_sub_epi16(VS, nvt);
|
||||
ncarry = _mm_adds_epu16(VS, vte);
|
||||
ncarry = _mm_cmpeq_epi16(diff, ncarry);
|
||||
nvce = _mm_cmpeq_epi16(VCE, zero);
|
||||
diff0 = _mm_cmpeq_epi16(diff, zero);
|
||||
lec1 = _mm_and_si128(diff0, ncarry);
|
||||
lec1 = _mm_and_si128(nvce, lec1);
|
||||
lec2 = _mm_or_si128(diff0, ncarry);
|
||||
lec2 = _mm_and_si128(VCE, lec2);
|
||||
leeq = _mm_or_si128(lec1, lec2);
|
||||
geeq = _mm_subs_epu16(vte, VS);
|
||||
geeq = _mm_cmpeq_epi16(geeq, zero);
|
||||
le = _mm_andnot_si128(VCOH, VCOL);
|
||||
le = _mm_blendv_epi8(VCCL, leeq, le);
|
||||
ge = _mm_or_si128(VCOL, VCOH);
|
||||
ge = _mm_blendv_epi8(geeq, VCCH, ge);
|
||||
mask = _mm_blendv_epi8(ge, le, VCOL);
|
||||
ACCL = _mm_blendv_epi8(VS, nvt, mask);
|
||||
VCCH = ge;
|
||||
VCCL = le;
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VCE = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVCH() -> void {
|
||||
v128 vte = VTe, nvt, diff, diff0, vtn, dlez, dgez, mask;
|
||||
VCOL = _mm_xor_si128(VS, vte);
|
||||
VCOL = _mm_cmplt_epi16(VCOL, zero);
|
||||
nvt = _mm_xor_si128(vte, VCOL);
|
||||
nvt = _mm_sub_epi16(nvt, VCOL);
|
||||
diff = _mm_sub_epi16(VS, nvt);
|
||||
diff0 = _mm_cmpeq_epi16(diff, zero);
|
||||
vtn = _mm_cmplt_epi16(vte, zero);
|
||||
dlez = _mm_cmpgt_epi16(diff, zero);
|
||||
dgez = _mm_or_si128(dlez, diff0);
|
||||
dlez = _mm_cmpeq_epi16(zero, dlez);
|
||||
VCCH = _mm_blendv_epi8(dgez, vtn, VCOL);
|
||||
VCCL = _mm_blendv_epi8(vtn, dlez, VCOL);
|
||||
VCE = _mm_cmpeq_epi16(diff, VCOL);
|
||||
VCE = _mm_and_si128(VCE, VCOL);
|
||||
VCOH = _mm_or_si128(diff0, VCE);
|
||||
VCOH = _mm_cmpeq_epi16(VCOH, zero);
|
||||
mask = _mm_blendv_epi8(VCCH, VCCL, VCOL);
|
||||
ACCL = _mm_blendv_epi8(VS, nvt, mask);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVCR() -> void {
|
||||
v128 vte = VTe, sign, dlez, dgez, nvt, mask;
|
||||
sign = _mm_xor_si128(VS, vte);
|
||||
sign = _mm_srai_epi16(sign, 15);
|
||||
dlez = _mm_and_si128(VS, sign);
|
||||
dlez = _mm_add_epi16(dlez, vte);
|
||||
VCCL = _mm_srai_epi16(dlez, 15);
|
||||
dgez = _mm_or_si128(VS, sign);
|
||||
dgez = _mm_min_epi16(dgez, vte);
|
||||
VCCH = _mm_cmpeq_epi16(dgez, vte);
|
||||
nvt = _mm_xor_si128(vte, sign);
|
||||
mask = _mm_blendv_epi8(VCCH, VCCL, sign);
|
||||
ACCL = _mm_blendv_epi8(VS, nvt, mask);
|
||||
VD = ACCL;
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
VCE = zero;
|
||||
}
|
||||
|
||||
auto RSP::instructionVEQ() -> void {
|
||||
v128 vte = VTe, eq;
|
||||
eq = _mm_cmpeq_epi16(VS, vte);
|
||||
VCCL = _mm_andnot_si128(VCOH, eq);
|
||||
ACCL = _mm_blendv_epi8(vte, VS, VCCL);
|
||||
VCCH = zero;
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVGE() -> void {
|
||||
v128 vte = VTe, eq, gt, es;
|
||||
eq = _mm_cmpeq_epi16(VS, vte);
|
||||
gt = _mm_cmpgt_epi16(VS, vte);
|
||||
es = _mm_and_si128(VCOH, VCOL);
|
||||
eq = _mm_andnot_si128(es, eq);
|
||||
VCCL = _mm_or_si128(gt, eq);
|
||||
ACCL = _mm_blendv_epi8(vte, VS, VCCL);
|
||||
VCCH = zero;
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVLT() -> void {
|
||||
v128 vte = VTe, eq, lt;
|
||||
eq = _mm_cmpeq_epi16(VS, vte);
|
||||
lt = _mm_cmplt_epi16(VS, vte);
|
||||
eq = _mm_and_si128(VCOH, eq);
|
||||
eq = _mm_and_si128(VCOL, eq);
|
||||
VCCL = _mm_or_si128(lt, eq);
|
||||
ACCL = _mm_blendv_epi8(vte, VS, VCCL);
|
||||
VCCH = zero;
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVMACF(bool U) -> void {
|
||||
v128 vte = VTe, lo, md, hi, carry, omask;
|
||||
lo = _mm_mullo_epi16(VS, vte);
|
||||
hi = _mm_mulhi_epi16(VS, vte);
|
||||
md = _mm_slli_epi16(hi, 1);
|
||||
carry = _mm_srli_epi16(lo, 15);
|
||||
hi = _mm_srai_epi16(hi, 15);
|
||||
md = _mm_or_si128(md, carry);
|
||||
lo = _mm_slli_epi16(lo, 1);
|
||||
omask = _mm_adds_epu16(ACCL, lo);
|
||||
ACCL = _mm_add_epi16(ACCL, lo);
|
||||
omask = _mm_cmpeq_epi16(ACCL, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
md = _mm_sub_epi16(md, omask);
|
||||
carry = _mm_cmpeq_epi16(md, zero);
|
||||
carry = _mm_and_si128(carry, omask);
|
||||
hi = _mm_sub_epi16(hi, carry);
|
||||
omask = _mm_adds_epu16(ACCM, md);
|
||||
ACCM = _mm_add_epi16(ACCM, md);
|
||||
omask = _mm_cmpeq_epi16(ACCM, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
ACCH = _mm_add_epi16(ACCH, hi);
|
||||
ACCH = _mm_sub_epi16(ACCH, omask);
|
||||
if(!U) {
|
||||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
VD = _mm_packs_epi32(lo, hi);
|
||||
} else {
|
||||
v128 mmask, hmask;
|
||||
mmask = _mm_srai_epi16(ACCM, 15);
|
||||
hmask = _mm_srai_epi16(ACCH, 15);
|
||||
md = _mm_or_si128(mmask, ACCM);
|
||||
omask = _mm_cmpgt_epi16(ACCH, zero);
|
||||
md = _mm_andnot_si128(hmask, md);
|
||||
VD = _mm_or_si128(omask, md);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionVMACQ() -> void {
|
||||
for(uint n : range(8)) {
|
||||
i32 product = ACCH.element(n) << 16 | ACCM.element(n) << 0;
|
||||
if(product < 0 && !(product & 1 << 5)) product += 32;
|
||||
else if(product >= 0 && !(product & 1 << 5)) product -= 32;
|
||||
ACCH.element(n) = product >> 16;
|
||||
ACCM.element(n) = product >> 0;
|
||||
ACCL.element(n) = 0; //unconfirmed
|
||||
VD.element(n) = sclamp<16>(product >> 1) & ~15;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionVMADH() -> void {
|
||||
v128 vte = VTe, lo, hi, omask;
|
||||
lo = _mm_mullo_epi16(VS, vte);
|
||||
hi = _mm_mulhi_epi16(VS, vte);
|
||||
omask = _mm_adds_epu16(ACCM, lo);
|
||||
ACCM = _mm_add_epi16(ACCM, lo);
|
||||
omask = _mm_cmpeq_epi16(ACCM, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
hi = _mm_sub_epi16(hi, omask);
|
||||
ACCH = _mm_add_epi16(ACCH, hi);
|
||||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
VD = _mm_packs_epi32(lo, hi);
|
||||
}
|
||||
|
||||
auto RSP::instructionVMADL() -> void {
|
||||
v128 vte = VTe, hi, omask, nhi, nmd, shi, smd, cmask, cval;
|
||||
hi = _mm_mulhi_epu16(VS, vte);
|
||||
omask = _mm_adds_epu16(ACCL, hi);
|
||||
ACCL = _mm_add_epi16(ACCL, hi);
|
||||
omask = _mm_cmpeq_epi16(ACCL, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
hi = _mm_sub_epi16(zero, omask);
|
||||
omask = _mm_adds_epu16(ACCM, hi);
|
||||
ACCM = _mm_add_epi16(ACCM, hi);
|
||||
omask = _mm_cmpeq_epi16(ACCM, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
ACCH = _mm_sub_epi16(ACCH, omask);
|
||||
nhi = _mm_srai_epi16(ACCH, 15);
|
||||
nmd = _mm_srai_epi16(ACCM, 15);
|
||||
shi = _mm_cmpeq_epi16(nhi, ACCH);
|
||||
smd = _mm_cmpeq_epi16(nhi, nmd);
|
||||
cmask = _mm_and_si128(smd, shi);
|
||||
cval = _mm_cmpeq_epi16(nhi, zero);
|
||||
VD = _mm_blendv_epi8(cval, ACCL, cmask);
|
||||
}
|
||||
|
||||
auto RSP::instructionVMADM() -> void {
|
||||
v128 vte = VTe, lo, hi, sign, vt, omask;
|
||||
lo = _mm_mullo_epi16(VS, vte);
|
||||
hi = _mm_mulhi_epu16(VS, vte);
|
||||
sign = _mm_srai_epi16(VS, 15);
|
||||
vt = _mm_and_si128(vte, sign);
|
||||
hi = _mm_sub_epi16(hi, vt);
|
||||
omask = _mm_adds_epu16(ACCL, lo);
|
||||
ACCL = _mm_add_epi16(ACCL, lo);
|
||||
omask = _mm_cmpeq_epi16(ACCL, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
hi = _mm_sub_epi16(hi, omask);
|
||||
omask = _mm_adds_epu16(ACCM, hi);
|
||||
ACCM = _mm_add_epi16(ACCM, hi);
|
||||
omask = _mm_cmpeq_epi16(ACCM, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
hi = _mm_srai_epi16(hi, 15);
|
||||
ACCH = _mm_add_epi16(ACCH, hi);
|
||||
ACCH = _mm_sub_epi16(ACCH, omask);
|
||||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
VD = _mm_packs_epi32(lo, hi);
|
||||
}
|
||||
|
||||
auto RSP::instructionVMADN() -> void {
|
||||
v128 vte = VTe, lo, hi, sign, vs, omask, nhi, nmd, shi, smd, cmask, cval;
|
||||
lo = _mm_mullo_epi16(VS, vte);
|
||||
hi = _mm_mulhi_epu16(VS, vte);
|
||||
sign = _mm_srai_epi16(vte, 15);
|
||||
vs = _mm_and_si128(VS, sign);
|
||||
hi = _mm_sub_epi16(hi, vs);
|
||||
omask = _mm_adds_epu16(ACCL, lo);
|
||||
ACCL = _mm_add_epi16(ACCL, lo);
|
||||
omask = _mm_cmpeq_epi16(ACCL, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
hi = _mm_sub_epi16(hi, omask);
|
||||
omask = _mm_adds_epu16(ACCM, hi);
|
||||
ACCM = _mm_add_epi16(ACCM, hi);
|
||||
omask = _mm_cmpeq_epi16(ACCM, omask);
|
||||
omask = _mm_cmpeq_epi16(omask, zero);
|
||||
hi = _mm_srai_epi16(hi, 15);
|
||||
ACCH = _mm_add_epi16(ACCH, hi);
|
||||
ACCH = _mm_sub_epi16(ACCH, omask);
|
||||
nhi = _mm_srai_epi16(ACCH, 15);
|
||||
nmd = _mm_srai_epi16(ACCM, 15);
|
||||
shi = _mm_cmpeq_epi16(nhi, ACCH);
|
||||
smd = _mm_cmpeq_epi16(nhi, nmd);
|
||||
cmask = _mm_and_si128(smd, shi);
|
||||
cval = _mm_cmpeq_epi16(nhi, zero);
|
||||
VD = _mm_blendv_epi8(cval, ACCL, cmask);
|
||||
}
|
||||
|
||||
auto RSP::instructionVMOV() -> void {
|
||||
u32 de = OP >> 11 & 7;
|
||||
ACCL = vte();
|
||||
VD.element(de) = ACCL.element(de);
|
||||
}
|
||||
|
||||
auto RSP::instructionVMRG() -> void {
|
||||
ACCL = _mm_blendv_epi8(VTe, VS, VCCL);
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVMUDH() -> void {
|
||||
v128 vte = VTe, lo, hi;
|
||||
ACCL = zero;
|
||||
ACCM = _mm_mullo_epi16(VS, vte);
|
||||
ACCH = _mm_mulhi_epi16(VS, vte);
|
||||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
VD = _mm_packs_epi32(lo, hi);
|
||||
}
|
||||
|
||||
auto RSP::instructionVMUDL() -> void {
|
||||
ACCL = _mm_mulhi_epu16(VS, VTe);
|
||||
ACCM = zero;
|
||||
ACCH = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVMUDM() -> void {
|
||||
v128 vte = VTe, sign, vt;
|
||||
ACCL = _mm_mullo_epi16(VS, vte);
|
||||
ACCM = _mm_mulhi_epu16(VS, vte);
|
||||
sign = _mm_srai_epi16(VS, 15);
|
||||
vt = _mm_and_si128(vte, sign);
|
||||
ACCM = _mm_sub_epi16(ACCM, vt);
|
||||
ACCH = _mm_srai_epi16(ACCM, 15);
|
||||
VD = ACCM;
|
||||
}
|
||||
|
||||
auto RSP::instructionVMUDN() -> void {
|
||||
v128 vte = VTe, sign, vs;
|
||||
ACCL = _mm_mullo_epi16(VS, vte);
|
||||
ACCM = _mm_mulhi_epu16(VS, vte);
|
||||
sign = _mm_srai_epi16(vte, 15);
|
||||
vs = _mm_and_si128(VS, sign);
|
||||
ACCM = _mm_sub_epi16(ACCM, vs);
|
||||
ACCH = _mm_srai_epi16(ACCM, 15);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVMULF(bool U) -> void {
|
||||
v128 vte = VTe, lo, hi, round, sign1, sign2, neq, eq, neg;
|
||||
lo = _mm_mullo_epi16(VS, vte);
|
||||
round = _mm_cmpeq_epi16(zero, zero);
|
||||
sign1 = _mm_srli_epi16(lo, 15);
|
||||
lo = _mm_add_epi16(lo, lo);
|
||||
round = _mm_slli_epi16(round, 15);
|
||||
hi = _mm_mulhi_epi16(VS, vte);
|
||||
sign2 = _mm_srli_epi16(lo, 15);
|
||||
ACCL = _mm_add_epi16(round, lo);
|
||||
sign1 = _mm_add_epi16(sign1, sign2);
|
||||
hi = _mm_slli_epi16(hi, 1);
|
||||
neq = _mm_cmpeq_epi16(VS, vte);
|
||||
ACCM = _mm_add_epi16(hi, sign1);
|
||||
neg = _mm_srai_epi16(ACCM, 15);
|
||||
if(!U) {
|
||||
eq = _mm_and_si128(neq, neg);
|
||||
ACCH = _mm_andnot_si128(neq, neg);
|
||||
VD = _mm_add_epi16(ACCM, eq);
|
||||
} else {
|
||||
ACCH = _mm_andnot_si128(neq, neg);
|
||||
hi = _mm_or_si128(ACCM, neg);
|
||||
VD = _mm_andnot_si128(ACCH, hi);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionVMULQ() -> void {
|
||||
v128 vte = VTe;
|
||||
for(uint n : range(8)) {
|
||||
i32 product = (i16)VS.element(n) * (i16)vte.element(n);
|
||||
if(product < 0) product += 31; //round
|
||||
ACCH.element(n) = product >> 16;
|
||||
ACCM.element(n) = product >> 0;
|
||||
ACCL.element(n) = 0; //unconfirmed
|
||||
VD.element(n) = sclamp<16>(product >> 1) & ~15;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionVNAND() -> void {
|
||||
ACCL = _mm_and_si128(VS, VTe);
|
||||
ACCL = _mm_xor_si128(ACCL, invert);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVNE() -> void {
|
||||
v128 vte = VTe, eq, ne;
|
||||
eq = _mm_cmpeq_epi16(VS, vte);
|
||||
ne = _mm_cmpeq_epi16(eq, zero);
|
||||
VCCL = _mm_and_si128(VCOH, eq);
|
||||
VCCL = _mm_or_si128(VCCL, ne);
|
||||
ACCL = _mm_blendv_epi8(vte, VS, VCCL);
|
||||
VCCH = zero;
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVNOP() -> void {
|
||||
}
|
||||
|
||||
auto RSP::instructionVNOR() -> void {
|
||||
ACCL = _mm_or_si128(VS, VTe);
|
||||
ACCL = _mm_xor_si128(ACCL, invert);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVNXOR() -> void {
|
||||
ACCL = _mm_xor_si128(VS, VTe);
|
||||
ACCL = _mm_xor_si128(ACCL, invert);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVOR() -> void {
|
||||
ACCL = _mm_or_si128(VS, VTe);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVRCP(bool L) -> void {
|
||||
i32 result = 0;
|
||||
i32 input = L && DIVDP ? DIVIN << 16 | VT.element(En & 7) : i16(VT.element(En & 7));
|
||||
i32 mask = input >> 31;
|
||||
i32 data = input ^ mask;
|
||||
if(input > -32768) data -= mask;
|
||||
if(data == 0) {
|
||||
result = 0x7fff'ffff;
|
||||
} else if(input == -32768) {
|
||||
result = 0xffff'0000;
|
||||
} else {
|
||||
u32 shift = __builtin_clz(data);
|
||||
u32 index = (u64(data) << shift & 0x7fc0'0000) >> 22;
|
||||
result = reciprocals[index];
|
||||
result = (0x10000 | result) << 14;
|
||||
result = result >> 31 - shift ^ mask;
|
||||
}
|
||||
DIVDP = 0;
|
||||
DIVOUT = result >> 16;
|
||||
ACCL = VTe;
|
||||
VD.element(DEn & 7) = result;
|
||||
}
|
||||
|
||||
auto RSP::instructionVRCPH() -> void {
|
||||
ACCL = VTe;
|
||||
DIVDP = 1;
|
||||
DIVIN = VT.element(En & 7);
|
||||
VD.element(DEn & 7) = DIVOUT;
|
||||
}
|
||||
|
||||
auto RSP::instructionVRND(bool D) -> void {
|
||||
v128 vte = VTe;
|
||||
for(uint n : range(8)) {
|
||||
i32 product = (i16)vte.element(n);
|
||||
if(VSn & 1) product <<= 16;
|
||||
i64 acc = 0;
|
||||
acc |= ACCH.element(n); acc <<= 16;
|
||||
acc |= ACCM.element(n); acc <<= 16;
|
||||
acc |= ACCL.element(n); acc <<= 16;
|
||||
acc >>= 16;
|
||||
if(D == 0 && acc < 0) acc += product;
|
||||
if(D == 1 && acc >= 0) acc += product;
|
||||
ACCH.element(n) = acc >> 32;
|
||||
ACCM.element(n) = acc >> 16;
|
||||
ACCL.element(n) = acc >> 0;
|
||||
VD.element(n) = acc >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionVRSQ(bool L) -> void {
|
||||
i32 result = 0;
|
||||
i32 input = L && DIVDP ? DIVIN << 16 | VT.element(En & 7) : i16(VT.element(En & 7));
|
||||
i32 mask = input >> 31;
|
||||
i32 data = input ^ mask;
|
||||
if(input > -32768) data -= mask;
|
||||
if(data == 0) {
|
||||
result = 0x7fff'ffff;
|
||||
} else if(input == -32768) {
|
||||
result = 0xffff'0000;
|
||||
} else {
|
||||
u32 shift = __builtin_clz(data);
|
||||
u32 index = (u64(data) << shift & 0x7fc0'0000) >> 22;
|
||||
result = inverseSquareRoots[index & 0x1fe | shift & 1];
|
||||
result = (0x10000 | result) << 14;
|
||||
result = result >> (31 - shift >> 1) ^ mask;
|
||||
}
|
||||
DIVDP = 0;
|
||||
DIVOUT = result >> 16;
|
||||
ACCL = VTe;
|
||||
VD.element(DEn & 7) = result;
|
||||
}
|
||||
|
||||
auto RSP::instructionVRSQH() -> void {
|
||||
ACCL = VTe;
|
||||
DIVDP = 1;
|
||||
DIVIN = VT.element(En & 7);
|
||||
VD.element(DEn & 7) = DIVOUT;
|
||||
}
|
||||
|
||||
auto RSP::instructionVSAR() -> void {
|
||||
switch(En) {
|
||||
case 0x8: VD = ACCH; break;
|
||||
case 0x9: VD = ACCM; break;
|
||||
case 0xa: VD = ACCL; break;
|
||||
default: VD = zero; break;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionVSUB() -> void {
|
||||
v128 vte = VTe, udiff, sdiff, ov;
|
||||
udiff = _mm_sub_epi16(vte, VCOL);
|
||||
sdiff = _mm_subs_epi16(vte, VCOL);
|
||||
ACCL = _mm_sub_epi16(VS, udiff);
|
||||
ov = _mm_cmpgt_epi16(sdiff, udiff);
|
||||
VD = _mm_subs_epi16(VS, sdiff);
|
||||
VD = _mm_adds_epi16(VD, ov);
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
}
|
||||
|
||||
auto RSP::instructionVSUBC() -> void {
|
||||
v128 vte = VTe, equal, udiff, diff0;
|
||||
udiff = _mm_subs_epu16(VS, vte);
|
||||
equal = _mm_cmpeq_epi16(VS, vte);
|
||||
diff0 = _mm_cmpeq_epi16(udiff, zero);
|
||||
VCOH = _mm_cmpeq_epi16(equal, zero);
|
||||
VCOL = _mm_andnot_si128(equal, diff0);
|
||||
ACCL = _mm_sub_epi16(VS, vte);
|
||||
VD = ACCL;
|
||||
}
|
||||
|
||||
auto RSP::instructionVXOR() -> void {
|
||||
ACCL = _mm_xor_si128(VS, VTe);
|
||||
VD = ACCL;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//{
|
||||
union v128 {
|
||||
struct { uint128_t u128; };
|
||||
struct { __m128i s128; };
|
||||
operator __m128i() const { return s128; }
|
||||
auto operator=(__m128i value) { s128 = value; }
|
||||
auto byte(uint index) -> u8& { return ((u8*)&u128)[15 - index]; }
|
||||
auto element(uint index) -> u16& { return ((u16*)&u128)[7 - index]; }
|
||||
};
|
||||
|
||||
//vu-instructions.cpp
|
||||
auto vte() -> v128;
|
||||
|
||||
struct COP2 {
|
||||
v128 r[32];
|
||||
v128 acch, accm, accl;
|
||||
v128 vcoh, vcol; //16-bit little endian
|
||||
v128 vcch, vccl; //16-bit little endian
|
||||
v128 vce; // 8-bit little endian
|
||||
i16 divin;
|
||||
i16 divout;
|
||||
bool divdp;
|
||||
} vu;
|
||||
|
||||
u16 reciprocals[512];
|
||||
u16 inverseSquareRoots[512];
|
||||
|
||||
static constexpr v128 zero{0};
|
||||
static constexpr v128 invert{u128(0) - 1};
|
||||
|
||||
//vu-instructions.cpp
|
||||
auto instructionCFC2() -> void;
|
||||
auto instructionCTC2() -> void;
|
||||
auto instructionLBV() -> void;
|
||||
auto instructionLDV() -> void;
|
||||
auto instructionLFV() -> void;
|
||||
auto instructionLHV() -> void;
|
||||
auto instructionLLV() -> void;
|
||||
auto instructionLPV() -> void;
|
||||
auto instructionLQV() -> void;
|
||||
auto instructionLRV() -> void;
|
||||
auto instructionLSV() -> void;
|
||||
auto instructionLTV() -> void;
|
||||
auto instructionLUV() -> void;
|
||||
auto instructionLWC2() -> void;
|
||||
auto instructionLWV() -> void;
|
||||
auto instructionMFC2() -> void;
|
||||
auto instructionMTC2() -> void;
|
||||
auto instructionSBV() -> void;
|
||||
auto instructionSDV() -> void;
|
||||
auto instructionSFV() -> void;
|
||||
auto instructionSHV() -> void;
|
||||
auto instructionSLV() -> void;
|
||||
auto instructionSPV() -> void;
|
||||
auto instructionSQV() -> void;
|
||||
auto instructionSRV() -> void;
|
||||
auto instructionSSV() -> void;
|
||||
auto instructionSTV() -> void;
|
||||
auto instructionSUV() -> void;
|
||||
auto instructionSWC2() -> void;
|
||||
auto instructionSWV() -> void;
|
||||
auto instructionVABS() -> void;
|
||||
auto instructionVADD() -> void;
|
||||
auto instructionVADDC() -> void;
|
||||
auto instructionVAND() -> void;
|
||||
auto instructionVCL() -> void;
|
||||
auto instructionVCH() -> void;
|
||||
auto instructionVCR() -> void;
|
||||
auto instructionVEQ() -> void;
|
||||
auto instructionVGE() -> void;
|
||||
auto instructionVLT() -> void;
|
||||
auto instructionVMACF(bool) -> void;
|
||||
auto instructionVMACQ() -> void;
|
||||
auto instructionVMADH() -> void;
|
||||
auto instructionVMADL() -> void;
|
||||
auto instructionVMADM() -> void;
|
||||
auto instructionVMADN() -> void;
|
||||
auto instructionVMOV() -> void;
|
||||
auto instructionVMRG() -> void;
|
||||
auto instructionVMUDH() -> void;
|
||||
auto instructionVMUDL() -> void;
|
||||
auto instructionVMUDM() -> void;
|
||||
auto instructionVMUDN() -> void;
|
||||
auto instructionVMULF(bool) -> void;
|
||||
auto instructionVMULQ() -> void;
|
||||
auto instructionVNAND() -> void;
|
||||
auto instructionVNE() -> void;
|
||||
auto instructionVNOP() -> void;
|
||||
auto instructionVNOR() -> void;
|
||||
auto instructionVNXOR() -> void;
|
||||
auto instructionVOR() -> void;
|
||||
auto instructionVRCP(bool) -> void;
|
||||
auto instructionVRCPH() -> void;
|
||||
auto instructionVRND(bool) -> void;
|
||||
auto instructionVRSQ(bool) -> void;
|
||||
auto instructionVRSQH() -> void;
|
||||
auto instructionVSAR() -> void;
|
||||
auto instructionVSUB() -> void;
|
||||
auto instructionVSUBC() -> void;
|
||||
auto instructionVXOR() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,42 @@
|
||||
auto RSP::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Instruction>("Instruction", "RSP");
|
||||
tracer.instruction->setAddressBits(12);
|
||||
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "RSP");
|
||||
|
||||
memory.dmem = parent->append<Node::Memory>("RSP DMEM");
|
||||
memory.dmem->setSize(4_KiB);
|
||||
memory.dmem->setRead([&](uint32 address) -> uint8 {
|
||||
return rsp.dmem.readByte(address);
|
||||
});
|
||||
memory.dmem->setWrite([&](uint32 address, uint8 data) -> void {
|
||||
return rsp.dmem.writeByte(address, data);
|
||||
});
|
||||
|
||||
memory.imem = parent->append<Node::Memory>("RSP IMEM");
|
||||
memory.imem->setSize(4_KiB);
|
||||
memory.imem->setRead([&](uint32 address) -> uint8 {
|
||||
return rsp.imem.readByte(address);
|
||||
});
|
||||
memory.imem->setWrite([&](uint32 address, uint8 data) -> void {
|
||||
return rsp.imem.writeByte(address, data);
|
||||
});
|
||||
}
|
||||
|
||||
auto RSP::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled()) {
|
||||
u32 address = rsp.pipeline.address & 0xfff;
|
||||
u32 instruction = rsp.pipeline.instruction;
|
||||
if(tracer.instruction->address(address)) {
|
||||
rsp.disassembler.showColors = 0;
|
||||
tracer.instruction->notify(rsp.disassembler.disassemble(address, instruction), {});
|
||||
rsp.disassembler.showColors = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
static const vector<string> registerNamesSCC = {
|
||||
"SP_PBUS_ADDRESS",
|
||||
"SP_DRAM_ADDRESS",
|
||||
"SP_READ_LENGTH",
|
||||
"SP_WRITE_LENGTH",
|
||||
"SP_STATUS",
|
||||
"SP_DMA_FULL",
|
||||
"SP_DMA_BUSY",
|
||||
"SP_SEMAPHORE",
|
||||
};
|
||||
|
||||
auto RSP::readSCC(u32 address) -> u32 {
|
||||
address = (address & 0x3ffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PBUS_ADDRESS
|
||||
data.bit( 0,11) = dma.memAddress;
|
||||
data.bit(12) = dma.memSource;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_DRAM_ADDRESS
|
||||
data.bit(0,23) = dma.dramAddress;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//SP_READ_LENGTH
|
||||
data.bit( 0,11) = dma.read.length;
|
||||
data.bit(12,19) = dma.read.count;
|
||||
data.bit(20,31) = dma.read.skip;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//SP_WRITE_LENGTH
|
||||
data.bit( 0,11) = dma.write.length;
|
||||
data.bit(12,19) = dma.write.count;
|
||||
data.bit(20,31) = dma.write.skip;
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//SP_STATUS
|
||||
data.bit( 0) = status.halted;
|
||||
data.bit( 1) = status.broken;
|
||||
data.bit( 2) = dma.busy;
|
||||
data.bit( 3) = dma.full;
|
||||
data.bit( 4) = status.full;
|
||||
data.bit( 5) = status.singleStep;
|
||||
data.bit( 6) = status.interruptOnBreak;
|
||||
data.bit( 7) = status.signal[0];
|
||||
data.bit( 8) = status.signal[1];
|
||||
data.bit( 9) = status.signal[2];
|
||||
data.bit(10) = status.signal[3];
|
||||
data.bit(11) = status.signal[4];
|
||||
data.bit(12) = status.signal[5];
|
||||
data.bit(13) = status.signal[6];
|
||||
data.bit(14) = status.signal[7];
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//SP_DMA_FULL
|
||||
data.bit(0) = dma.full;
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//SP_DMA_BUSY
|
||||
data.bit(0) = dma.busy;
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//SP_SEMAPHORE
|
||||
data.bit(0) = status.semaphore;
|
||||
status.semaphore = 1;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNamesSCC(address, "SP_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RSP::writeSCC(u32 address, uint32 data) -> void {
|
||||
address = (address & 0x3ffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PBUS_ADDRESS
|
||||
dma.memAddress = data.bit( 0,11) & ~3;
|
||||
dma.memSource = data.bit(12);
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_DRAM_ADDRESS
|
||||
dma.dramAddress = data.bit(0,23) & ~7;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//SP_READ_LENGTH
|
||||
dma.read.length = data.bit( 0,11);
|
||||
dma.read.count = data.bit(12,19);
|
||||
dma.read.skip = data.bit(20,31);
|
||||
|
||||
auto& memory = dma.memSource == 0 ? dmem : imem;
|
||||
auto length = (dma.read.length | 7) + 1;
|
||||
for(u32 count : range(dma.read.count + 1)) {
|
||||
for(u32 offset = 0; offset < length; offset += 4) {
|
||||
u32 data = rdram.ram.readWord(dma.dramAddress + offset);
|
||||
memory.writeWord(dma.memAddress + offset, data);
|
||||
}
|
||||
dma.dramAddress += length + dma.read.skip;
|
||||
dma.memAddress += length;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//SP_WRITE_LENGTH
|
||||
dma.write.length = data.bit( 0,11);
|
||||
dma.write.count = data.bit(12,19);
|
||||
dma.write.skip = data.bit(20,31);
|
||||
|
||||
auto& memory = dma.memSource == 0 ? dmem : imem;
|
||||
auto length = (dma.write.length | 7) + 1;
|
||||
for(u32 count : range(dma.write.count + 1)) {
|
||||
for(u32 offset = 0; offset < length; offset += 4) {
|
||||
u32 data = memory.readWord(dma.memAddress + offset);
|
||||
rdram.ram.writeWord(dma.dramAddress + offset, data);
|
||||
}
|
||||
dma.dramAddress += length + dma.write.skip;
|
||||
dma.memAddress += length;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//SP_STATUS
|
||||
if(data.bit( 0)) status.halted = 0;
|
||||
if(data.bit( 1)) status.halted = 1;
|
||||
if(data.bit( 2)) status.broken = 0;
|
||||
if(data.bit( 3)) mi.lower(MI::IRQ::SP);
|
||||
if(data.bit( 4)) mi.raise(MI::IRQ::SP);
|
||||
if(data.bit( 5)) status.singleStep = 0;
|
||||
if(data.bit( 6)) status.singleStep = 1;
|
||||
if(data.bit( 7)) status.interruptOnBreak = 0;
|
||||
if(data.bit( 8)) status.interruptOnBreak = 1;
|
||||
if(data.bit( 9)) status.signal[0] = 0;
|
||||
if(data.bit(10)) status.signal[0] = 1;
|
||||
if(data.bit(11)) status.signal[1] = 0;
|
||||
if(data.bit(12)) status.signal[1] = 1;
|
||||
if(data.bit(13)) status.signal[2] = 0;
|
||||
if(data.bit(14)) status.signal[2] = 1;
|
||||
if(data.bit(15)) status.signal[3] = 0;
|
||||
if(data.bit(16)) status.signal[3] = 1;
|
||||
if(data.bit(17)) status.signal[4] = 0;
|
||||
if(data.bit(18)) status.signal[4] = 1;
|
||||
if(data.bit(19)) status.signal[5] = 0;
|
||||
if(data.bit(20)) status.signal[5] = 1;
|
||||
if(data.bit(21)) status.signal[6] = 0;
|
||||
if(data.bit(22)) status.signal[6] = 1;
|
||||
if(data.bit(23)) status.signal[7] = 0;
|
||||
if(data.bit(24)) status.signal[7] = 1;
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//SP_DMA_FULL (read-only)
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//SP_DMA_BUSY (read-only)
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//SP_SEMAPHORE
|
||||
if(!data.bit(0)) status.semaphore = 0;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNamesSCC(address, "SP_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
static const vector<string> registerNames = {
|
||||
"SP_PC_REG",
|
||||
"SP_IBIST",
|
||||
};
|
||||
|
||||
auto RSP::readIO(u32 address) -> u32 {
|
||||
address = (address & 0x7ffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PC_REG
|
||||
data.bit(0,11) = core.pc;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_IBIST
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "SP_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RSP::writeIO(u32 address, uint32 data) -> void {
|
||||
address = (address & 0x7ffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PC_REG
|
||||
if(branch.state == Branch::Take) {
|
||||
branch.pc = data.bit(0,11) & ~3;
|
||||
} else {
|
||||
core.pc = data.bit(0,11) & ~3;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_IBIST
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "SP_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
+18
-10
@@ -3,33 +3,41 @@
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
RSP rsp;
|
||||
#include "core/core.cpp"
|
||||
#include "io.cpp"
|
||||
#include "io-scc.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RSP::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("RSP");
|
||||
|
||||
stream = node->append<Node::Stream>("RSP");
|
||||
stream->setChannels(2);
|
||||
stream->setFrequency(48000.0);
|
||||
dmem.allocate(4_KiB);
|
||||
imem.allocate(4_KiB);
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto RSP::unload() -> void {
|
||||
dmem.reset();
|
||||
imem.reset();
|
||||
node = {};
|
||||
stream = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto RSP::main() -> void {
|
||||
stream->sample(0.0, 0.0);
|
||||
step(1);
|
||||
if(status.halted) return step(96);
|
||||
instruction();
|
||||
step(3);
|
||||
}
|
||||
|
||||
auto RSP::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
auto RSP::power() -> void {
|
||||
Thread::create(48'000, {&RSP::main, this});
|
||||
Thread::reset();
|
||||
powerCore();
|
||||
dma = {};
|
||||
status = {};
|
||||
}
|
||||
|
||||
}
|
||||
+53
-1
@@ -2,7 +2,25 @@
|
||||
|
||||
struct RSP : Thread {
|
||||
Node::Component node;
|
||||
Node::Stream stream;
|
||||
Memory dmem;
|
||||
Memory imem;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto instruction() -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Instruction instruction;
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
|
||||
struct Memory {
|
||||
Node::Memory dmem;
|
||||
Node::Memory imem;
|
||||
} memory;
|
||||
} debugger;
|
||||
|
||||
//rsp.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -13,8 +31,42 @@ struct RSP : Thread {
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
|
||||
//io-scc.cpp
|
||||
auto readSCC(u32 address) -> u32;
|
||||
auto writeSCC(u32 address, uint32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct DMA {
|
||||
uint1 memSource = 0;
|
||||
uint12 memAddress = 0;
|
||||
uint24 dramAddress = 0;
|
||||
uint1 busy = 0;
|
||||
uint1 full = 0;
|
||||
|
||||
struct Transfer {
|
||||
uint12 length = 0;
|
||||
uint12 skip = 0;
|
||||
uint8 count = 0;
|
||||
} read, write;
|
||||
} dma;
|
||||
|
||||
struct Status {
|
||||
uint1 semaphore = 0;
|
||||
uint1 halted = 1;
|
||||
uint1 broken = 0;
|
||||
uint1 full = 0;
|
||||
uint1 singleStep = 0;
|
||||
uint1 interruptOnBreak = 0;
|
||||
uint1 signal[8] = {};
|
||||
} status;
|
||||
|
||||
#include "core/core.hpp"
|
||||
};
|
||||
|
||||
extern RSP rsp;
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user