Compare commits

...
31 Commits
Author SHA1 Message Date
Near 842ea44b90 Update to ares v113 release.
- N64: improved TLB emulation (Super Mario 64 is now fully playable)
  - lucia: implement analog axis support
2020-05-24 05:01:00 +00:00
Near e67dfac166 Update to ares v112r30 release.
Many N64 games are now playable.

  - N64: VSUB should clear VCO
  - N64: corrected VRCP(L) and VRSQ(L) instructions
  - N64: many other smaller various fixes
2020-05-21 15:17:00 +00:00
Near a5d2ee5e5f Update to ares v112r29 release.
- N64: added missing BLTZAL, BGEZAL instructions
  - N64: added missing SLT, SLTI instructions (they were erroneously
    aliased to SLTU, SLTIU)
  - N64: mask low two bits of PC when writing to SP_PC_REG
  - N64: refactored RSP DMA; only mask the low two bits SP_PBUS_ADDRESS
  - N64: RSP DMA seemingly updates SP_PBUS_ADDRESS and SP_DRAM_ADDRESS
2020-05-20 05:13:00 +00:00
Near bef70d2550 Update to ares v112r28 release.
- N64: implemented missing RSP vector load/store instructions
  - N64: fixed RSP MFC2 and MTC2 instructions
  - N64: rewrote and simplified CPU TLB handling
2020-05-20 00:51:00 +00:00
Near f656e317c8 Update to ares v112r27 release.
- N64: improved CPU disassembler
  - N64: added RSP disassembler
  - N64: corrected reads from SP_SEMAPHORE
  - N64: improved PIF joypad polling functionality
  - N64: added I/O logging to trace logger
2020-05-18 23:35:00 +00:00
Near 494b1cb0ba Update to ares v112r26 release.
- N64: CPU and RSP instruction improvements
  - N64: improved CPU and RSP branch handling
2020-05-18 03:37:00 +00:00
Near be25bb110c Update to ares v112r25 release.
- N64: added skeleton I/O reading and writing for DPS registers
  - N64: added skeleton RDP renderer
2020-05-17 06:27:00 +00:00
Near 1f59ef7457 Update to ares v112r24 release.
- N64: added PIF boot ROM emulation
  - N64: detected NUS-CIC revision based on ROM bootcode checksum
  - N64: added skeleton I/O reading and writing for all VI and PI
    registers
2020-05-17 04:15:00 +00:00
Near 74e6d389a6 Update to ares v112r23 release.
This WIP separates the RSP SCC registers so that RDP rendering commands
are not processed inside of the RSP. The register interface is shared
between the SCC and the CPU bus, so there is no technically correct
location for these handlers.

  - N64: moved RSP SCC registers to outer RSP and RDP I/O handlers
2020-05-16 00:53:00 +00:00
Near 9449b78675 Update to ares v112r22 release.
- N64: added RDRAM and interrupt sources to debugger
  - N64: emulated basic RDP commands to detect sync commands for DP IRQ
    generation
2020-05-15 01:40:00 +00:00
Near 388f555304 Update to ares v112r21 release.
- N64: emulated FPU FR=0 mode; fixes intro sound in Namco Museum
2020-05-14 22:51:00 +00:00
Near 4cf677063c Update to ares v112r20 release.
- N64: improved AI FIFO emulation
  - N64: emulated VT[e] addressing in RSP vector unit
2020-05-13 14:59:00 +00:00
Near 890b56f84d Update to ares v112r19 release.
- N64: improved TLB emulation and made it much faster (but limited to
    32-bit mode without ASIDs)
2020-05-13 02:23:00 +00:00
Near ddd3a439ff Update to ares v112r18 release.
- N64: improved audio playback (AI_LENGTH &=~ 7, not (AI_LENGTH | 7) +
    1)
  - N64: improved exception handling (ERL bit isn't set on standard
    exceptions)
  - N64: added very incomplete, inital TLB emulation
  - N64: various improvements to the disasssembler (including FPU
    instruction support)
  - N64: improved SI emulation (Namco Museum input now works)
2020-05-12 10:13:00 +00:00
Near 318beb509f Update to ares v112r17 release.
This WIP boots the first official Nintendo 64 title, Namco Museum. It
remains to be playable, however.

  - N64: improved interrupt handling
  - N64: implemented basic state machine handling for PI serial data
    transfers
  - N64: resume previously executed instruction after ERET for non-
    interrupt exceptions
  - N64: various other improvements
2020-05-11 06:56:00 +00:00
Near 6f6e2ea260 Update to ares v112r16 release.
- N64: skip delay slot when executing ERET instruction
  - N64: improve IRQ handling (always update CPU SCC Cause bits when
    toggling MI IRQs)
  - N64: audio emulation improvements
2020-05-10 19:57:00 +00:00
Near 67cf63b532 Update to ares v112r15 release.
- N64: implemented SI DMA
  - N64: implemented basic AI audio emulation
  - N64: various improvements and bugfixes
2020-05-10 04:26:00 +00:00
Near a427fe2a71 Update to ares v112r14 release.
- N64: implemented CPU SCC ERET instruction
  - N64: improved interrupt support slightly
  - N64: rewrote CPU disassembler to be more sophisticated
2020-05-09 00:52:00 +00:00
Near e6c42199aa Update to ares v112r13 release.
- N64: RSP vector instruction fixes
  - N64: started on initial exception and interrupt support
2020-05-07 22:51:00 +00:00
Near b224caf4bd Update to ares v112r12 release.
- N64: implemented all official RSP vector instructions
  - N64: implemented reserved RSP vector instructions VMULQ, VMACQ,
    VRNDP, VRNDN
  - N64: implemented RSP instructions MFC2 and MFC2
2020-05-07 07:49:00 +00:00
Near bd565eb982 Update to ares v112r11 release.
- N64: core now requires SSE 4.1 support (for vector instructions)
  - N64: implemented VABS, VADD, VADDC, VAND, VCL, VCH, VMOV, VNAND,
    VNOP, VSAR, VSUB, VSUBC instructions
2020-05-05 22:51:00 +00:00
Near f149b5d2a5 Update to ares v112r10 release.
- N64: removed libco completely, using direct cycle counters now
    instead
  - N64: emulated RSP DMA between IRAM/DRAM and RDRAM
  - N64: emulated RSP core R4000 instructions
  - N64: started working on RSP COP2 vector instructions
2020-05-05 03:30:00 +00:00
Near 32d8abd372 Update to ares v112r09 release.
Emulation is now far enough along to complete the boot loading code and
begin executing game code. The "hello world" CPU rendering demo now runs
correctly in both 15bpp and 24bpp output modes.

  - N64: implemented skeleton interfaces (MI, VI, AI, PI, RI, SI)
  - N64: implemented PI DMA
  - N64: added basic frame buffer rendering (320x240 only)
2020-05-03 17:30:00 +00:00
Near 5ef188c876 Update to ares v112r08 release.
- N64: correct JAL and JALR instructions
2020-05-02 17:41:00 +00:00
Near 590773508e Update to ares v112r07 release.
- N64: redesigned CPU register casting (use a union class instead of
    #defines)
  - N64: completed CPU coprocessor 1 FPU
2020-05-02 15:11:00 +00:00
Near 2f24221be9 Update to ares v112r06 release.
- N64: much faster endian decoding
  - N64: implemented CPU coprocessor 0 skeleton (excluding MMU)
  - N64: implemented CPU coprocessor 1 skeleton (excluding FPU)
2020-05-01 21:17:00 +00:00
Near e7b120f2e5 Update to ares v112r05 release.
- N64: implemented bus skeleton
  - N64: simulated the PIF boot ROM (partially)
2020-04-30 18:58:00 +00:00
Near 4a1084179b Update to ares v112r04 release.
- N64: merged the VR4300 core into the CPU class
  - mia: added N64 deinterleaving support
2020-04-30 13:30:00 +00:00
Near e9bd3dda68 Update to ares v112r03 release.
- VR4300: implemented most of the general instructions
2020-04-29 19:19:00 +00:00
Near c1f9897a03 Update to ares v112r02 release.
- VR4300: implemented register-immediate instructions
  - VR4300: implemented half of the general instructions
  - ares: added Port::supported() -> vector<string> API
  - nall: improve checks in directory::exists() and directory::create()
2020-04-29 15:52:00 +00:00
Near 291a0ea8de Update to ares v112r01 release.
- VR4300: implemented special instructions
2020-04-28 19:20:00 +00:00
133 changed files with 9141 additions and 151 deletions

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -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";
+3
View File
@@ -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;
};
+15
View File
@@ -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>;
-2
View File
@@ -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;
};
}
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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
View File
@@ -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
+61
View File
@@ -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);
}
}
+52
View File
@@ -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;
+9
View File
@@ -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);
}
}
+81
View File
@@ -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)});
}
}
+3
View File
@@ -0,0 +1,3 @@
auto AI::serialize(serializer& s) -> void {
Thread::serialize(s);
}
+3
View File
@@ -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 = {};
}
+5 -1
View File
@@ -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;
};
+1
View File
@@ -2,6 +2,7 @@ struct Controller {
Node::Peripheral node;
virtual ~Controller() = default;
virtual auto read() -> uint32 { return 0; }
};
#include "port.hpp"
+57
View File
@@ -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;
}
+18
View File
@@ -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;
};
+1
View File
@@ -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 {
+54
View File
@@ -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;
}
}
+66
View File
@@ -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);
//};
+538
View File
@@ -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;
}
+145
View File
@@ -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;
//};
+475
View File
@@ -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)...};
}
+33
View File
@@ -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};
//};
+23
View File
@@ -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};
//};
+300
View File
@@ -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
+118
View File
@@ -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;
}
}
+93
View File
@@ -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;
//};
+317
View File
@@ -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
}
+89
View File
@@ -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;
}
+21
View File
@@ -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;
//};
+47
View File
@@ -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
+59
View File
@@ -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;
}
+253
View File
@@ -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;
}
}
+38
View File
@@ -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;
}
+186
View File
@@ -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;
//};
+2
View File
@@ -0,0 +1,2 @@
auto CPU::serializeR4300(serializer& s) -> void {
}
+23 -6
View File
@@ -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
View File
@@ -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;
+31
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
auto CPU::serialize(serializer& s) -> void {
VR4300::serialize(s);
Thread::serialize(s);
serializeR4300(s);
}
+231
View File
@@ -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;
}
+7
View File
@@ -0,0 +1,7 @@
#include <n64/n64.hpp>
namespace ares::Nintendo64 {
Bus bus;
}
+100
View File
@@ -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;
+16
View File
@@ -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);
}
}
+86
View File
@@ -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)});
}
}
+74
View File
@@ -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;
}
}
+62
View File
@@ -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;
+2
View File
@@ -0,0 +1,2 @@
auto MI::serialize(serializer& s) -> void {
}
+50 -4
View File
@@ -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>
+18
View File
@@ -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);
}
}
+185
View File
@@ -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)});
}
}
+59
View File
@@ -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);
}
}
}
+53
View File
@@ -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;
+2
View File
@@ -0,0 +1,2 @@
auto PI::serialize(serializer& s) -> void {
}
+16
View File
@@ -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);
}
}
+127
View File
@@ -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)});
}
}
+69
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+382
View File
@@ -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;
}
+18
View File
@@ -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);
}
}
+110
View File
@@ -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)});
}
}
+29
View File
@@ -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();
}
}
+37
View File
@@ -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;
+2
View File
@@ -0,0 +1,2 @@
auto RDRAM::serialize(serializer& s) -> void {
}
+9
View File
@@ -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);
}
}
+92
View File
@@ -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)});
}
}
+23
View File
@@ -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 {
}
}
+29
View File
@@ -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;
+2
View File
@@ -0,0 +1,2 @@
auto RI::serialize(serializer& s) -> void {
}
+40
View File
@@ -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);
}
}
+22
View File
@@ -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"
//};
+152
View File
@@ -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;
}
+69
View File
@@ -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;
//};
+518
View File
@@ -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)...};
}
+35
View File
@@ -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};
//};
+291
View File
@@ -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;
}
}
+6
View File
@@ -0,0 +1,6 @@
//{
struct Pipeline {
u32 address;
u32 instruction;
} pipeline;
//};
+95
View File
@@ -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
+9
View File
@@ -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);
}
+9
View File
@@ -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;
//};
+2
View File
@@ -0,0 +1,2 @@
auto RSP::serializeCore(serializer& s) -> void {
}
+949
View File
@@ -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;
}
+101
View File
@@ -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;
//};
+42
View File
@@ -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);
}
}
+177
View File
@@ -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)});
}
}
+44
View File
@@ -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
View File
@@ -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
View File
@@ -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