mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-12 18:06:03 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01184dc6bd | ||
|
|
17f1f24dc2 | ||
|
|
e2a5547ae7 | ||
|
|
6447903966 | ||
|
|
f97a1f954e | ||
|
|
5c6ef3eb4d | ||
|
|
ce2abced51 | ||
|
|
50b62604a8 | ||
|
|
fb855d62af | ||
|
|
5f035496bb | ||
|
|
bfdb287505 | ||
|
|
a37e5ba378 | ||
|
|
b12ac07464 | ||
|
|
46bba9b798 | ||
|
|
b1b4452d23 | ||
|
|
60bddfc33f | ||
|
|
c2a9cc9fbc | ||
|
|
f376ce8499 | ||
|
|
0ed46cc2b1 | ||
|
|
d9ea05c4c4 | ||
|
|
a4caa85c0b | ||
|
|
8b0359aa78 | ||
|
|
ba05897d75 | ||
|
|
6cd757dae8 | ||
|
|
fb9859ad2f | ||
|
|
f518bcb374 | ||
|
|
20469c66e9 | ||
|
|
3559a7659b | ||
|
|
1f45125d45 | ||
|
|
03f7fe2b25 | ||
|
|
a05937bfa4 | ||
|
|
d9d208d615 |
No files matched your search
@@ -46,6 +46,10 @@ ifneq ($(filter $(cores),md),)
|
||||
include $(ares.path)/md/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),ps1),)
|
||||
include $(ares.path)/ps1/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),pce),)
|
||||
include $(ares.path)/pce/GNUmakefile
|
||||
endif
|
||||
|
||||
Whitespace-only changes.
@@ -1,5 +1,5 @@
|
||||
database
|
||||
revision: 2020-01-21
|
||||
revision: 2020-06-07
|
||||
|
||||
//Boards (Production)
|
||||
|
||||
@@ -587,7 +587,7 @@ board: SHVC-YJ0N-01
|
||||
//Boards (Generic)
|
||||
|
||||
database
|
||||
revision: 2020-01-01
|
||||
revision: 2020-06-07
|
||||
|
||||
board: ARM-LOROM-RAM
|
||||
memory type=ROM content=Program
|
||||
@@ -887,6 +887,16 @@ board: OBC1-LOROM-RAM
|
||||
map address=70-71,f0-f1:6000-7fff,e000-ffff mask=0xe000
|
||||
memory type=RAM content=Save
|
||||
|
||||
board: SA1
|
||||
processor architecture=W65C816S
|
||||
map address=00-3f,80-bf:2200-23ff
|
||||
mcu
|
||||
map address=00-3f,80-bf:8000-ffff mask=0x408000
|
||||
map address=c0-ff:0000-ffff
|
||||
memory type=ROM content=Program
|
||||
memory type=RAM content=Internal
|
||||
map address=00-3f,80-bf:3000-37ff size=0x800
|
||||
|
||||
board: SA1-RAM
|
||||
processor architecture=W65C816S
|
||||
map address=00-3f,80-bf:2200-23ff
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include <ares/debug/debug.cpp>
|
||||
#include <ares/node/node.cpp>
|
||||
#include <ares/resource/resource.cpp>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <nall/adaptive-array.hpp>
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bump-allocator.hpp>
|
||||
#include <nall/chrono.hpp>
|
||||
#include <nall/directory.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include <nall/set.hpp>
|
||||
#include <nall/shared-pointer.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/terminal.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
#include <nall/unique-pointer.hpp>
|
||||
#include <nall/variant.hpp>
|
||||
@@ -58,6 +60,7 @@ namespace ares {
|
||||
#include <ares/information.hpp>
|
||||
#include <ares/types.hpp>
|
||||
#include <ares/random.hpp>
|
||||
#include <ares/debug/debug.hpp>
|
||||
#include <ares/node/node.hpp>
|
||||
#include <ares/platform.hpp>
|
||||
#include <ares/interface.hpp>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace ares {
|
||||
|
||||
Debug _debug;
|
||||
|
||||
auto Debug::reset() -> void {
|
||||
_unimplementedNotices.reset();
|
||||
}
|
||||
|
||||
auto Debug::_unimplemented(const string& text) -> void {
|
||||
//only warn about missing unimplemented functionality once per type
|
||||
if(_unimplementedNotices.find(text)) return;
|
||||
_unimplementedNotices.append(text);
|
||||
|
||||
print(terminal::color::magenta("[unimplemented] "), text, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace ares {
|
||||
|
||||
struct Debug {
|
||||
static constexpr bool enabled = true;
|
||||
|
||||
auto reset() -> void;
|
||||
|
||||
template<typename... P> auto unimplemented(P&&... p) -> void {
|
||||
return _unimplemented({forward<P>(p)...});
|
||||
}
|
||||
|
||||
private:
|
||||
auto _unimplemented(const string&) -> void;
|
||||
|
||||
vector<string> _unimplementedNotices;
|
||||
};
|
||||
|
||||
extern Debug _debug;
|
||||
|
||||
}
|
||||
|
||||
#define debug(function, ...) if constexpr(ares::_debug.enabled) ares::_debug.function(__VA_ARGS__)
|
||||
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "113";
|
||||
static const string License = "GPLv3+";
|
||||
static const string Website = "https://ares.dev";
|
||||
static const string Name = "ares";
|
||||
static const string Version = "115";
|
||||
static const string Copyright = "ares team";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
static const string Website = "ares.dev";
|
||||
static const string WebsiteURI = "https://ares.dev";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "112";
|
||||
static const string SerializerVersion = "115";
|
||||
}
|
||||
@@ -237,7 +237,7 @@ auto SM83::instruction() -> void {
|
||||
op(0xee, XOR_Direct_Data, A)
|
||||
op(0xef, RST_Implied, 0x28)
|
||||
op(0xf0, LDH_Direct_Address, A)
|
||||
op(0xf1, POP_Direct, AF)
|
||||
op(0xf1, POP_Direct_AF, AF)
|
||||
op(0xf2, LDH_Direct_Indirect, A, C)
|
||||
op(0xf3, DI)
|
||||
op(0xf5, PUSH_Direct, AF)
|
||||
|
||||
@@ -271,6 +271,10 @@ auto SM83::instructionPOP_Direct(uint16& data) -> void {
|
||||
data = pop();
|
||||
}
|
||||
|
||||
auto SM83::instructionPOP_Direct_AF(uint16& data) -> void {
|
||||
data = pop() & ~15; //flag bits 0-3 are forced to zero
|
||||
}
|
||||
|
||||
auto SM83::instructionPUSH_Direct(uint16& data) -> void {
|
||||
idle();
|
||||
push(data);
|
||||
|
||||
File renamed without changes.
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "registers.hpp"
|
||||
#include "memory.cpp"
|
||||
#include "algorithms.cpp"
|
||||
#include "instruction.cpp"
|
||||
|
||||
@@ -115,6 +115,7 @@ struct SM83 {
|
||||
auto instructionOR_Direct_Direct(uint8&, uint8&) -> void;
|
||||
auto instructionOR_Direct_Indirect(uint8&, uint16&) -> void;
|
||||
auto instructionPOP_Direct(uint16&) -> void;
|
||||
auto instructionPOP_Direct_AF(uint16&) -> void;
|
||||
auto instructionPUSH_Direct(uint16&) -> void;
|
||||
auto instructionRES_Index_Direct(uint3, uint8&) -> void;
|
||||
auto instructionRES_Index_Indirect(uint3, uint16&) -> void;
|
||||
|
||||
File renamed without changes.
@@ -4,7 +4,7 @@
|
||||
namespace ares {
|
||||
|
||||
#include "disassembler.cpp"
|
||||
#include "registers.cpp"
|
||||
#include "registers.hpp"
|
||||
#include "memory.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "algorithms.cpp"
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//ASCII 16kbit
|
||||
|
||||
struct ASC16 : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x7fff) data = rom.read(bank[0] << 14 | (uint14)address);
|
||||
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank[1] << 14 | (uint14)address);
|
||||
|
||||
if(address >= 0xc000 && address <= 0xffff) data = rom.read(bank[0] << 14 | (uint14)address);
|
||||
if(address >= 0x0000 && address <= 0x3fff) data = rom.read(bank[1] << 14 | (uint14)address);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x6000 && address <= 0x6fff) bank[0] = data;
|
||||
if(address >= 0x7000 && address <= 0xbfff) bank[1] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0x0f; //R-Type = 0x0f; others = 0x00
|
||||
bank[1] = 0x00;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[2];
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
//ASCII 8kbit
|
||||
|
||||
struct ASC8 : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x5fff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0x6000 && address <= 0x7fff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x8000 && address <= 0x9fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0xa000 && address <= 0xbfff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
if(address >= 0xc000 && address <= 0xdfff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0xe000 && address <= 0xffff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x0000 && address <= 0x1fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0x2000 && address <= 0x3fff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x6000 && address <= 0x67ff) bank[0] = data;
|
||||
if(address >= 0x6800 && address <= 0x6fff) bank[1] = data;
|
||||
if(address >= 0x7000 && address <= 0x77ff) bank[2] = data;
|
||||
if(address >= 0x7800 && address <= 0x7fff) bank[3] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 0;
|
||||
bank[2] = 0;
|
||||
bank[3] = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[4];
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
namespace Board {
|
||||
|
||||
#include "asc16.cpp"
|
||||
#include "asc8.cpp"
|
||||
#include "cross-blaim.cpp"
|
||||
#include "konami.cpp"
|
||||
#include "konami-scc.cpp"
|
||||
#include "linear.cpp"
|
||||
#include "super-lode-runner.cpp"
|
||||
#include "super-pierrot.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<uint8>& memory, Markup::Node node) -> bool {
|
||||
if(!node) return false;
|
||||
memory.allocate(node["size"].natural());
|
||||
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
|
||||
if(auto fp = platform->open(cartridge.node, name, File::Read, File::Required)) {
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::load(Memory::Writable<uint8>& memory, Markup::Node node) -> bool {
|
||||
if(!node) return false;
|
||||
memory.allocate(node["size"].natural());
|
||||
if(node["volatile"]) return true;
|
||||
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
|
||||
if(auto fp = platform->open(cartridge.node, name, File::Read)) {
|
||||
memory.load(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::save(Memory::Writable<uint8>& memory, Markup::Node node) -> bool {
|
||||
if(!node) return false;
|
||||
if(node["volatile"]) return true;
|
||||
auto name = string{node["content"].string(), ".", node["type"].string()}.downcase();
|
||||
if(auto fp = platform->open(cartridge.node, name, File::Write)) {
|
||||
memory.save(fp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Interface::main() -> void {
|
||||
cartridge.step(system.colorburst());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Board {
|
||||
|
||||
struct Interface {
|
||||
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
|
||||
virtual ~Interface() = default;
|
||||
virtual auto load(Markup::Node) -> void {}
|
||||
virtual auto save(Markup::Node) -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
virtual auto main() -> void;
|
||||
virtual auto read(uint16 address, uint8 data) -> uint8 { return data; }
|
||||
virtual auto write(uint16 address, uint8 data) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
|
||||
auto load(Memory::Readable<uint8>&, Markup::Node) -> bool;
|
||||
auto load(Memory::Writable<uint8>&, Markup::Node) -> bool;
|
||||
auto save(Memory::Writable<uint8>&, Markup::Node) -> bool;
|
||||
|
||||
Cartridge& cartridge;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//Cross Blaim
|
||||
|
||||
struct CrossBlaim : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(mode == 0 || mode == 1) {
|
||||
if(address >> 14 == 0) data = rom.read((uint14)address + 16_KiB);
|
||||
if(address >> 14 == 1) data = rom.read((uint14)address);
|
||||
if(address >> 14 == 2) data = rom.read((uint14)address + 16_KiB);
|
||||
if(address >> 14 == 3) data = rom.read((uint14)address + 16_KiB);
|
||||
}
|
||||
|
||||
if(mode == 2) {
|
||||
if(address >> 14 == 0) data = 0xff;
|
||||
if(address >> 14 == 1) data = rom.read((uint14)address);
|
||||
if(address >> 14 == 2) data = rom.read((uint14)address + 32_KiB);
|
||||
if(address >> 14 == 3) data = 0xff;
|
||||
}
|
||||
|
||||
if(mode == 3) {
|
||||
if(address >> 14 == 0) data = 0xff;
|
||||
if(address >> 14 == 1) data = rom.read((uint14)address);
|
||||
if(address >> 14 == 2) data = rom.read((uint14)address + 48_KiB);
|
||||
if(address >> 14 == 3) data = 0xff;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
mode = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.integer(mode);
|
||||
}
|
||||
|
||||
uint2 mode;
|
||||
};
|
||||
@@ -0,0 +1,252 @@
|
||||
//Konami (with Sound Creative Chip)
|
||||
|
||||
struct KonamiSCC : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
Node::Stream stream;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
|
||||
stream = cartridge.node->append<Node::Stream>("SCC");
|
||||
stream->setChannels(1);
|
||||
stream->setFrequency(system.colorburst() / 16.0);
|
||||
stream->addHighPassFilter(20.0, 1);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto main() -> void override {
|
||||
int sample = 256 * 5;
|
||||
|
||||
for(auto& voice : voices) {
|
||||
if(voice.frequency <= 8) continue; //voice is halted when frequency < 9
|
||||
|
||||
voice.clock += 32;
|
||||
while(voice.clock > voice.frequency) {
|
||||
voice.clock -= voice.frequency + 1;
|
||||
voice.counter++;
|
||||
}
|
||||
sample += voice.wave[voice.counter] * voice.volume * voice.key >> 3;
|
||||
}
|
||||
|
||||
stream->sample(mixer[sample] / 32768.0);
|
||||
cartridge.step(16);
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x5fff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0x6000 && address <= 0x7fff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x8000 && address <= 0x9fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0xa000 && address <= 0xbfff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
if(address >= 0xc000 && address <= 0xdfff) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >= 0xe000 && address <= 0xffff) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >= 0x0000 && address <= 0x1fff) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >= 0x2000 && address <= 0x3fff) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
|
||||
address.bit(8) = 0; //SCC ignores A8
|
||||
|
||||
if(address >= 0x9800 && address <= 0x981f) {
|
||||
data = voices[0].wave[address & 0x1f];
|
||||
if(test.bit(6)) data = voices[0].wave[address + voices[0].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9820 && address <= 0x983f) {
|
||||
data = voices[1].wave[address & 0x1f];
|
||||
if(test.bit(6)) data = voices[1].wave[address + voices[1].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9840 && address <= 0x985f) {
|
||||
data = voices[2].wave[address & 0x1f];
|
||||
if(test.bit(6)) data = voices[2].wave[address + voices[2].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9860 && address <= 0x987f) {
|
||||
data = voices[3].wave[address & 0x1f];
|
||||
if(test.bit(6) || test.bit(7)) data = voices[3].wave[address + voices[3 + test.bit(6)].counter & 0x1f];
|
||||
}
|
||||
|
||||
if(address >= 0x9880 && address <= 0x98df) {
|
||||
data = 0xff;
|
||||
}
|
||||
|
||||
if(address >= 0x98e0 && address <= 0x98ff) {
|
||||
//reading the test register sets it to 0xff
|
||||
data = test = 0xff;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x5000 && address <= 0x57ff) bank[0] = data;
|
||||
if(address >= 0x7000 && address <= 0x77ff) bank[1] = data;
|
||||
if(address >= 0x9000 && address <= 0x97ff) bank[2] = data;
|
||||
if(address >= 0xb000 && address <= 0xb7ff) bank[3] = data;
|
||||
|
||||
address.bit(8) = 0; //SCC ignores A8
|
||||
|
||||
if(address >= 0x9800 && address <= 0x981f && !test.bit(6)) {
|
||||
voices[0].wave[address & 0x1f] = data;
|
||||
}
|
||||
|
||||
if(address >= 0x9820 && address <= 0x983f && !test.bit(6)) {
|
||||
voices[1].wave[address & 0x1f] = data;
|
||||
}
|
||||
|
||||
if(address >= 0x9840 && address <= 0x985f && !test.bit(6)) {
|
||||
voices[2].wave[address & 0x1f] = data;
|
||||
}
|
||||
|
||||
if(address >= 0x9860 && address <= 0x987f && !test.bit(6) && !test.bit(7)) {
|
||||
voices[3].wave[address & 0x1f] = data;
|
||||
voices[4].wave[address & 0x1f] = data; //shares data with wave channel 3
|
||||
}
|
||||
|
||||
if(address == 0x9880) {
|
||||
voices[0].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[0].frequency < 9) voices[0].clock = 0;
|
||||
if(test.bit(5)) voices[0].clock = 0, voices[0].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9881) {
|
||||
voices[0].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[0].frequency < 9) voices[0].clock = 0;
|
||||
if(test.bit(5)) voices[0].clock = 0, voices[0].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9882) {
|
||||
voices[1].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[1].frequency < 9) voices[1].clock = 0;
|
||||
if(test.bit(5)) voices[1].clock = 0, voices[1].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9883) {
|
||||
voices[1].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[1].frequency < 9) voices[1].clock = 0;
|
||||
if(test.bit(5)) voices[1].clock = 0, voices[1].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9884) {
|
||||
voices[2].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[2].frequency < 9) voices[2].clock = 0;
|
||||
if(test.bit(5)) voices[2].clock = 0, voices[2].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9885) {
|
||||
voices[2].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[2].frequency < 9) voices[2].clock = 0;
|
||||
if(test.bit(5)) voices[2].clock = 0, voices[2].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9886) {
|
||||
voices[3].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[3].frequency < 9) voices[3].clock = 0;
|
||||
if(test.bit(5)) voices[3].clock = 0, voices[3].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9887) {
|
||||
voices[3].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[3].frequency < 9) voices[3].clock = 0;
|
||||
if(test.bit(5)) voices[3].clock = 0, voices[3].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9888) {
|
||||
voices[4].frequency.bit(0, 7) = data.bit(0,7);
|
||||
if(voices[4].frequency < 9) voices[4].clock = 0;
|
||||
if(test.bit(5)) voices[4].clock = 0, voices[4].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x9889) {
|
||||
voices[4].frequency.bit(8,11) = data.bit(0,3);
|
||||
if(voices[4].frequency < 9) voices[4].clock = 0;
|
||||
if(test.bit(5)) voices[4].clock = 0, voices[4].counter = 0;
|
||||
}
|
||||
|
||||
if(address == 0x988a) {
|
||||
voices[0].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988b) {
|
||||
voices[1].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988c) {
|
||||
voices[2].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988d) {
|
||||
voices[3].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988e) {
|
||||
voices[4].volume = data.bit(0,3);
|
||||
}
|
||||
|
||||
if(address == 0x988f) {
|
||||
voices[0].key = data.bit(0);
|
||||
voices[1].key = data.bit(1);
|
||||
voices[2].key = data.bit(2);
|
||||
voices[3].key = data.bit(3);
|
||||
voices[4].key = data.bit(4);
|
||||
}
|
||||
|
||||
if(address >= 0x98e0 && address <= 0x98ff) test = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 1;
|
||||
bank[2] = 2;
|
||||
bank[3] = 3;
|
||||
voices[0] = {};
|
||||
voices[1] = {};
|
||||
voices[2] = {};
|
||||
voices[3] = {};
|
||||
voices[4] = {};
|
||||
test = 0;
|
||||
|
||||
mixer.resize(512 * 5);
|
||||
auto table = mixer.data() + 256 * 5;
|
||||
for(int n : range(256 * 5)) {
|
||||
int16 volume = n * 8 * 16 / 5;
|
||||
table[+n] = +volume;
|
||||
table[-n] = -volume;
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
for(auto& voice : voices) {
|
||||
s.integer(voice.clock);
|
||||
s.integer(voice.frequency);
|
||||
s.integer(voice.counter);
|
||||
s.integer(voice.volume);
|
||||
s.integer(voice.key);
|
||||
s.array(voice.wave);
|
||||
}
|
||||
s.integer(test);
|
||||
}
|
||||
|
||||
uint8 bank[4];
|
||||
|
||||
struct Voice {
|
||||
uint16 clock;
|
||||
uint12 frequency;
|
||||
uint5 counter;
|
||||
uint4 volume;
|
||||
uint1 key;
|
||||
int8 wave[32];
|
||||
} voices[5];
|
||||
|
||||
uint8 test;
|
||||
|
||||
//unserialized:
|
||||
vector<int16> mixer;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
//Konami (without Sound Creative Chip)
|
||||
|
||||
struct Konami : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >> 13 == 2) data = rom.read(bank[0] << 13 | (uint13)address);
|
||||
if(address >> 13 == 3) data = rom.read(bank[1] << 13 | (uint13)address);
|
||||
if(address >> 13 == 4) data = rom.read(bank[2] << 13 | (uint13)address);
|
||||
if(address >> 13 == 5) data = rom.read(bank[3] << 13 | (uint13)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >> 13 == 2) bank[0] = 0;
|
||||
if(address >> 13 == 3) bank[1] = data;
|
||||
if(address >> 13 == 4) bank[2] = data;
|
||||
if(address >> 13 == 5) bank[3] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 1;
|
||||
bank[2] = 0;
|
||||
bank[3] = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[4];
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
struct Linear : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
data = rom.read(address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
//Super Lode Runner
|
||||
//(not working: requires MSX BASIC)
|
||||
|
||||
struct SuperLodeRunner : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank << 14 | (uint14)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x0000 && address <= 0x3fff) bank = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.integer(bank);
|
||||
}
|
||||
|
||||
uint8 bank;
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
//Super Pierrot
|
||||
//(not working)
|
||||
|
||||
struct SuperPierrot : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<uint8> rom;
|
||||
|
||||
auto load(Markup::Node document) -> void override {
|
||||
auto board = document["game/board"];
|
||||
Interface::load(rom, board["memory(type=ROM,content=Program)"]);
|
||||
}
|
||||
|
||||
auto save(Markup::Node document) -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(uint16 address, uint8 data) -> uint8 override {
|
||||
if(address >= 0x4000 && address <= 0x7fff) data = rom.read(bank[0] << 14 | (uint14)address);
|
||||
if(address >= 0x8000 && address <= 0xbfff) data = rom.read(bank[1] << 14 | (uint14)address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(uint16 address, uint8 data) -> void override {
|
||||
if(address >= 0x4000 && address <= 0x4fff) bank[0] = data;
|
||||
if(address >= 0x6000 && address <= 0x6fff) bank[0] = data;
|
||||
if(address >= 0x8000 && address <= 0x8fff) bank[0] = data;
|
||||
if(address >= 0xa000 && address <= 0xafff) bank[0] = data;
|
||||
|
||||
if(address >= 0x5000 && address <= 0x5fff) bank[1] = data;
|
||||
if(address >= 0x7000 && address <= 0x7fff) bank[1] = data;
|
||||
if(address >= 0x9000 && address <= 0x9fff) bank[1] = data;
|
||||
if(address >= 0xa000 && address <= 0xafff) bank[1] = data;
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
bank[0] = 0;
|
||||
bank[1] = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
s.array(bank);
|
||||
}
|
||||
|
||||
uint8 bank[2];
|
||||
};
|
||||
@@ -4,6 +4,7 @@ namespace ares::MSX {
|
||||
|
||||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
Cartridge& expansion = expansionSlot.cartridge;
|
||||
#include "board/board.cpp"
|
||||
#include "slot.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@@ -23,87 +24,56 @@ auto Cartridge::connect() -> void {
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
information.name = document["game/label"].string();
|
||||
information.region = document["game/region"].string();
|
||||
information.board = document["game/board"].string();
|
||||
|
||||
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
|
||||
rom.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(node, "program.rom", File::Read, File::Required)) {
|
||||
rom.load(fp);
|
||||
}
|
||||
}
|
||||
|
||||
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
|
||||
ram.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(node, "save.ram", File::Read)) {
|
||||
ram.load(fp);
|
||||
}
|
||||
}
|
||||
if(information.board == "ASC16") board = new Board::ASC16{*this};
|
||||
if(information.board == "ASC8") board = new Board::ASC8{*this};
|
||||
if(information.board == "CrossBlaim") board = new Board::CrossBlaim{*this};
|
||||
if(information.board == "Konami") board = new Board::Konami{*this};
|
||||
if(information.board == "KonamiSCC") board = new Board::KonamiSCC{*this};
|
||||
if(information.board == "Linear") board = new Board::Linear{*this};
|
||||
if(information.board == "SuperLodeRunner") board = new Board::SuperLodeRunner{*this};
|
||||
if(information.board == "SuperPierrot") board = new Board::SuperPierrot{*this};
|
||||
if(!board) board = new Board::KonamiSCC{*this};
|
||||
board->load(document);
|
||||
|
||||
power();
|
||||
}
|
||||
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
rom.reset();
|
||||
ram.reset();
|
||||
node = {};
|
||||
if(board) board->unload(), board.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
if(!node) return;
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
board->save(document);
|
||||
}
|
||||
|
||||
if(auto memory = document["game/board/memory(type=RAM,content=Save)"]) {
|
||||
if(auto fp = platform->open(node, "save.ram", File::Write)) {
|
||||
ram.save(fp);
|
||||
}
|
||||
}
|
||||
auto Cartridge::main() -> void {
|
||||
if(board) return board->main();
|
||||
step(system.colorburst());
|
||||
}
|
||||
|
||||
auto Cartridge::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
Thread::create(system.colorburst(), {&Cartridge::main, this});
|
||||
if(board) board->power();
|
||||
}
|
||||
|
||||
uint2 RB; //Cross Blaim
|
||||
uint8 B8K[4]={0,1,0,0}; //Konami 8K (non-SCC)
|
||||
|
||||
auto Cartridge::read(uint16 address) -> uint8 {
|
||||
if(!rom) return 0xff;
|
||||
|
||||
/*
|
||||
if(RB==0||RB==1){
|
||||
if(address>>14==0)return rom.read((uint14)address+16_KiB);
|
||||
if(address>>14==1)return rom.read((uint14)address);
|
||||
if(address>>14==2)return rom.read((uint14)address+16_KiB);
|
||||
if(address>>14==3)return rom.read((uint14)address+16_KiB);
|
||||
}
|
||||
if(RB==2){
|
||||
if(address>>14==0)return 0xff;
|
||||
if(address>>14==1)return rom.read((uint14)address);
|
||||
if(address>>14==2)return rom.read((uint14)address+32_KiB);
|
||||
if(address>>14==3)return 0xff;
|
||||
}
|
||||
if(RB==3){
|
||||
if(address>>14==0)return 0xff;
|
||||
if(address>>14==1)return rom.read((uint14)address);
|
||||
if(address>>14==2)return rom.read((uint14)address+48_KiB);
|
||||
if(address>>14==3)return 0xff;
|
||||
}
|
||||
*/
|
||||
|
||||
if(address>>13==2)return rom.read(B8K[0]<<13|(uint13)address);
|
||||
if(address>>13==3)return rom.read(B8K[1]<<13|(uint13)address);
|
||||
if(address>>13==4)return rom.read(B8K[2]<<13|(uint13)address);
|
||||
if(address>>13==5)return rom.read(B8K[3]<<13|(uint13)address);
|
||||
|
||||
return rom.read(address);
|
||||
if(board) return board->read(address, 0xff);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::write(uint16 address, uint8 data) -> void {
|
||||
RB = data;
|
||||
if(address>>13==2)B8K[0]=0;
|
||||
if(address>>13==3)B8K[1]=data;
|
||||
if(address>>13==4)B8K[2]=data;
|
||||
if(address>>13==5)B8K[3]=data;
|
||||
if(board) return board->write(address, data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
struct Cartridge {
|
||||
struct Cartridge;
|
||||
#include "board/board.hpp"
|
||||
|
||||
struct Cartridge : Thread {
|
||||
Node::Peripheral node;
|
||||
Memory::Readable<uint8> rom;
|
||||
Memory::Writable<uint8> ram;
|
||||
@@ -13,6 +16,8 @@ struct Cartridge {
|
||||
auto disconnect() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto read(uint16 address) -> uint8;
|
||||
@@ -21,11 +26,14 @@ struct Cartridge {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
unique_pointer<Board::Interface> board;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
string manifest;
|
||||
string name;
|
||||
string region;
|
||||
string board;
|
||||
} information;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(ram) s.array(ram.data(), ram.size());
|
||||
if(board) board->serialize(s);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
ares.objects += ares-n64-interface ares-n64-memory ares-n64-system
|
||||
ares.objects += ares-n64-cartridge ares-n64-controller
|
||||
ares.objects += ares-n64-cartridge ares-n64-controller ares-n64-dd
|
||||
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
|
||||
@@ -9,6 +9,7 @@ $(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-dd.o: $(ares.path)/n64/dd/dd.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
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
//Audio Interface
|
||||
|
||||
struct AI : Thread {
|
||||
struct AI : Thread, Memory::IO<AI> {
|
||||
Node::Component node;
|
||||
Node::Stream stream;
|
||||
|
||||
@@ -23,8 +23,8 @@ struct AI : Thread {
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
+3
-2
@@ -7,7 +7,7 @@ static const vector<string> registerNames = {
|
||||
"AI_BITRATE",
|
||||
};
|
||||
|
||||
auto AI::readIO(u32 address) -> u32 {
|
||||
auto AI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
@@ -31,8 +31,9 @@ auto AI::readIO(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto AI::writeIO(u32 address, uint32 data) -> void {
|
||||
auto AI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//AI_DRAM_ADDRESS
|
||||
|
||||
@@ -29,6 +29,8 @@ auto Cartridge::connect() -> void {
|
||||
if(auto fp = platform->open(node, "program.rom", File::Read, File::Required)) {
|
||||
rom.load(fp);
|
||||
}
|
||||
} else {
|
||||
rom.allocate(16);
|
||||
}
|
||||
|
||||
power();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
Memory rom;
|
||||
Memory::Readable rom;
|
||||
|
||||
auto manifest() const -> string { return information.manifest; }
|
||||
auto name() const -> string { return information.name; }
|
||||
|
||||
@@ -38,8 +38,8 @@ auto Gamepad::read() -> uint32 {
|
||||
platform->input(start);
|
||||
|
||||
uint32 data;
|
||||
data.byte(0) = y->value() >> 8;
|
||||
data.byte(1) = x->value() >> 8;
|
||||
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();
|
||||
|
||||
+100
-3
@@ -3,13 +3,105 @@
|
||||
#include "scc-registers.cpp"
|
||||
#include "fpu-registers.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "decoder.cpp"
|
||||
#include "cpu-instructions.cpp"
|
||||
#include "scc-instructions.cpp"
|
||||
#include "fpu-instructions.cpp"
|
||||
#include "recompiler.cpp"
|
||||
#include "disassembler.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
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) {
|
||||
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 {
|
||||
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;
|
||||
step(2);
|
||||
return raiseException(0);
|
||||
}
|
||||
}
|
||||
|
||||
//devirtualize the program counter
|
||||
auto address = readAddress(PC)(0);
|
||||
|
||||
if constexpr(Accuracy::CPU::Interpreter == 0) {
|
||||
auto block = recompiler.block(address);
|
||||
block->execute();
|
||||
step(block->step);
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::CPU::Interpreter == 1) {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = bus.readWord(address);
|
||||
debugger.instruction();
|
||||
//instructionDebug();
|
||||
decoderEXECUTE();
|
||||
instructionEpilogue();
|
||||
step(2);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionEpilogue() -> bool {
|
||||
GPR[0].u64 = 0;
|
||||
|
||||
if(--scc.random.index < scc.wired.index) {
|
||||
scc.random.index = 31;
|
||||
}
|
||||
|
||||
switch(branch.state) {
|
||||
case Branch::Step: PC += 4; return 0;
|
||||
case Branch::Take: PC += 4; branch.delaySlot(); return 0;
|
||||
case Branch::DelaySlot: PC = branch.pc; branch.reset(); return 1;
|
||||
case Branch::Exception: branch.reset(); return 1;
|
||||
case Branch::Discard: PC += 8; branch.reset(); return 1;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::instructionDebug() -> void {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = readWord(pipeline.address)(0);
|
||||
|
||||
static vector<bool> mask;
|
||||
if(!mask) mask.resize(0x0800'0000);
|
||||
if(mask[(PC & 0x1fff'ffff) >> 2]) return;
|
||||
mask[(PC & 0x1fff'ffff) >> 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::powerR4300(bool reset) -> void {
|
||||
for(uint n : range(32)) GPR[n].u64 = 0;
|
||||
scc = {};
|
||||
@@ -17,11 +109,16 @@ auto CPU::powerR4300(bool reset) -> void {
|
||||
fpu = {};
|
||||
LO.u64 = 0;
|
||||
HI.u64 = 0;
|
||||
GPR[Core::Register::SP].u64 = i32(0xa400'1ff0);
|
||||
PC = i32(0xbfc00000);
|
||||
GPR[Core::Register::SP].u64 = u32(0xa400'1ff0);
|
||||
PC = u32(0xbfc0'0000);
|
||||
branch.reset();
|
||||
fesetround(FE_TONEAREST);
|
||||
context.setMode();
|
||||
|
||||
if constexpr(Accuracy::CPU::Interpreter == 0) {
|
||||
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill);
|
||||
recompiler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Context::setMode() -> void {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
//{
|
||||
//core.cpp
|
||||
auto raiseException(uint, uint = 0) -> void;
|
||||
auto instruction() -> void;
|
||||
auto instructionEpilogue() -> bool;
|
||||
auto instructionDebug() -> void;
|
||||
auto powerR4300(bool reset) -> void;
|
||||
|
||||
//memory.cpp
|
||||
@@ -16,15 +20,16 @@
|
||||
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;
|
||||
//decoder.cpp
|
||||
auto decoderEXECUTE() -> void;
|
||||
auto decoderSPECIAL() -> void;
|
||||
auto decoderREGIMM() -> void;
|
||||
auto decoderCOP0() -> void;
|
||||
auto decoderCOP1() -> void;
|
||||
|
||||
auto instructionCOP2() -> void;
|
||||
auto instructionCOP3() -> void;
|
||||
auto instructionINVALID() -> void;
|
||||
|
||||
enum Interrupt : uint {
|
||||
Software0 = 0,
|
||||
@@ -58,6 +63,7 @@
|
||||
#include "cpu.hpp"
|
||||
#include "scc.hpp"
|
||||
#include "fpu.hpp"
|
||||
#include "recompiler.hpp"
|
||||
#include "disassembler.hpp"
|
||||
|
||||
static constexpr bool Endian = 1; //0 = little, 1 = big
|
||||
|
||||
@@ -1,46 +1,102 @@
|
||||
auto CPU::instructionADD() -> void {
|
||||
//if(RS.u32 > ~RT.u32) return exception.arithmeticOverflow();
|
||||
RD.u64 = i32(RS.u32 + RT.u32);
|
||||
auto CPU::instructionADD(r64& rd, cr64& rs, cr64& rt) -> 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::instructionADDI(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
//if(rs.i32 > ~imm) return exception.arithmeticOverflow();
|
||||
rt.u64 = i32(rs.u32 + imm);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDIU() -> void {
|
||||
RT.u64 = i32(RS.u32 + IMMi16);
|
||||
auto CPU::instructionADDIU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = i32(rs.u32 + imm);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDU() -> void {
|
||||
RD.u64 = i32(RS.u32 + RT.u32);
|
||||
auto CPU::instructionADDU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = i32(rs.u32 + rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionAND() -> void {
|
||||
RD.u64 = RS.u64 & RT.u64;
|
||||
auto CPU::instructionAND(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 & rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionANDI() -> void {
|
||||
RT.u64 = RS.u64 & IMMu16;
|
||||
auto CPU::instructionANDI(r64& rt, cr64& rs, u16 imm) -> void {
|
||||
rt.u64 = rs.u64 & imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionB(bool take) -> void {
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
auto CPU::instructionBEQ(cr64& rs, cr64& rt, i16 imm) -> void {
|
||||
if(rs.u64 == rt.u64) branch.take(PC + 4 + (imm << 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));
|
||||
auto CPU::instructionBEQL(cr64& rs, cr64& rt, i16 imm) -> void {
|
||||
if(rs.u64 == rt.u64) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBL(bool take) -> void {
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
auto CPU::instructionBGEZ(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBGEZAL(cr64& rs, i16 imm) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBGEZALL(cr64& rs, i16 imm) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBGEZL(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 >= 0) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBGTZ(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 > 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBGTZL(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 > 0) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBLEZ(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 <= 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBLEZL(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 <= 0) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBLTZ(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBLTZAL(cr64& rs, i16 imm) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBLTZALL(cr64& rs, i16 imm) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBLTZL(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 < 0) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionBNE(cr64& rs, cr64& rt, i16 imm) -> void {
|
||||
if(rs.u64 != rt.u64) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionBNEL(cr64& rs, cr64& rt, i16 imm) -> void {
|
||||
if(rs.u64 != rt.u64) branch.take(PC + 4 + (imm << 2));
|
||||
else branch.discard();
|
||||
}
|
||||
|
||||
@@ -48,10 +104,8 @@ auto CPU::instructionBREAK() -> void {
|
||||
exception.breakpoint();
|
||||
}
|
||||
|
||||
auto CPU::instructionCACHE() -> void {
|
||||
auto CPU::instructionCACHE(u8 cache, u8 operation) -> void {
|
||||
//todo
|
||||
uint cache = OP >> 16 & 3;
|
||||
uint operation = OP >> 18 & 3;
|
||||
if(cache == 0) {
|
||||
//instruction
|
||||
if(operation == 0) {
|
||||
@@ -99,440 +153,432 @@ auto CPU::instructionCACHE() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDADD() -> void {
|
||||
//if(RS.u64 > ~RT.u64) return exception.arithmeticOverflow();
|
||||
RD.u64 = RS.u64 + RT.u64;
|
||||
auto CPU::instructionDADD(r64& rd, cr64& rs, cr64& rt) -> 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::instructionDADDI(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
//if(rs.i64 > ~imm) return exception.arithmeticOverflow();
|
||||
rt.u64 = rs.u64 + imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionDADDIU() -> void {
|
||||
RT.u64 = RS.u64 + i64(IMMi16);
|
||||
auto CPU::instructionDADDIU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = rs.u64 + imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionDADDU() -> void {
|
||||
RD.u64 = RS.u64 + RT.u64;
|
||||
auto CPU::instructionDADDU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 + rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDDIV() -> void {
|
||||
if(RT.i64) {
|
||||
auto CPU::instructionDDIV(cr64& rs, cr64& rt) -> 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);
|
||||
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;
|
||||
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;
|
||||
auto CPU::instructionDDIVU(cr64& rs, cr64& rt) -> void {
|
||||
if(rt.u64) {
|
||||
LO.u64 = rs.u64 / rt.u64;
|
||||
HI.u64 = rs.u64 % rt.u64;
|
||||
} else {
|
||||
LO.u64 = -1;
|
||||
HI.u64 = RS.u64;
|
||||
HI.u64 = rs.u64;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDIV() -> void {
|
||||
if(RT.i32) {
|
||||
auto CPU::instructionDIV(cr64& rs, cr64& rt) -> 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));
|
||||
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;
|
||||
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);
|
||||
auto CPU::instructionDIVU(cr64& rs, cr64& rt) -> 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;
|
||||
HI.u64 = rs.i32;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDMULT() -> void {
|
||||
u128 result = RS.i128() * RT.i128();
|
||||
auto CPU::instructionDMULT(cr64& rs, cr64& rt) -> 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();
|
||||
auto CPU::instructionDMULTU(cr64& rs, cr64& rt) -> 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::instructionDSLL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.u64 << sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLL32() -> void {
|
||||
RD.u64 = RT.u64 << SA + 32;
|
||||
auto CPU::instructionDSLLV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.u64 << (rs.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLLV() -> void {
|
||||
RD.u64 = RT.u64 << (RS.u32 & 63);
|
||||
auto CPU::instructionDSRA(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.i64 >> sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRA() -> void {
|
||||
RD.u64 = RT.i64 >> SA;
|
||||
auto CPU::instructionDSRAV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.i64 >> (rs.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRA32() -> void {
|
||||
RD.u64 = RT.i64 >> SA + 32;
|
||||
auto CPU::instructionDSRL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.u64 >> sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRAV() -> void {
|
||||
RD.u64 = RT.i64 >> (RS.u32 & 63);
|
||||
auto CPU::instructionDSRLV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.u64 >> (rs.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRL() -> void {
|
||||
RD.u64 = RT.u64 >> SA;
|
||||
auto CPU::instructionDSUB(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
//if(rs.u64 < rt.u64) return exception.arithmeticOverflow();
|
||||
rd.u64 = rs.u64 - rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRL32() -> void {
|
||||
RD.u64 = RT.u64 >> SA + 32;
|
||||
auto CPU::instructionDSUBU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 - rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRLV() -> void {
|
||||
RD.u64 = RT.u64 >> (RS.u32 & 63);
|
||||
auto CPU::instructionJ(u32 imm) -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
|
||||
}
|
||||
|
||||
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));
|
||||
auto CPU::instructionJAL(u32 imm) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionJALR() -> void {
|
||||
branch.take(RS.u32);
|
||||
RD.u64 = i32(PC + 8);
|
||||
auto CPU::instructionJALR(r64& rd, cr64& rs) -> void {
|
||||
rd.u64 = i32(PC + 8);
|
||||
branch.take(rs.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionJR() -> void {
|
||||
branch.take(RS.u32);
|
||||
auto CPU::instructionJR(cr64& rs) -> void {
|
||||
branch.take(rs.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionLB() -> void {
|
||||
if(auto data = readByte(RS.u32 + IMMi16)) RT.u64 = i8(*data);
|
||||
auto CPU::instructionLB(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readByte(rs.u32 + imm)) rt.u64 = i8(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLBU() -> void {
|
||||
if(auto data = readByte(RS.u32 + IMMi16)) RT.u64 = u8(*data);
|
||||
auto CPU::instructionLBU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readByte(rs.u32 + imm)) rt.u64 = u8(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLD() -> void {
|
||||
if(auto data = readDouble(RS.u32 + IMMi16)) RT.u64 = *data;
|
||||
auto CPU::instructionLD(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readDouble(rs.u32 + imm)) rt.u64 = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionLDL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionLDL(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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;
|
||||
rt.u64 = rt.u64 & ~mask | *data << shift;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLDR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionLDR(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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;
|
||||
rt.u64 = rt.u64 & ~mask | *data >> shift;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLH() -> void {
|
||||
if(auto data = readHalf(RS.u32 + IMMi16)) RT.u64 = i16(*data);
|
||||
auto CPU::instructionLH(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readHalf(rs.u32 + imm)) rt.u64 = i16(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLHU() -> void {
|
||||
if(auto data = readHalf(RS.u32 + IMMi16)) RT.u64 = u16(*data);
|
||||
auto CPU::instructionLHU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readHalf(rs.u32 + imm)) rt.u64 = u16(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLL() -> void {
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) {
|
||||
RT.u64 = i32(*data);
|
||||
auto CPU::instructionLL(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readWord(rs.u32 + imm)) {
|
||||
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;
|
||||
auto CPU::instructionLLD(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readDouble(rs.u32 + imm)) {
|
||||
rt.u64 = *data;
|
||||
scc.ll = tlb.physicalAddress >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLUI() -> void {
|
||||
RT.u64 = i32(IMMu16 << 16);
|
||||
auto CPU::instructionLUI(r64& rt, u16 imm) -> void {
|
||||
rt.u64 = i32(imm << 16);
|
||||
}
|
||||
|
||||
auto CPU::instructionLW() -> void {
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) RT.u64 = i32(*data);
|
||||
auto CPU::instructionLW(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readWord(rs.u32 + imm)) rt.u64 = i32(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionLWL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionLWL(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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);
|
||||
rt.u64 = i32(rt.u32 & ~mask | *data << shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLWR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionLWR(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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);
|
||||
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::instructionLWU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readWord(rs.u32 + imm)) rt.u64 = u32(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionMFHI() -> void {
|
||||
RD.u64 = HI.u64;
|
||||
auto CPU::instructionMFHI(r64& rd) -> void {
|
||||
rd.u64 = HI.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMFLO() -> void {
|
||||
RD.u64 = LO.u64;
|
||||
auto CPU::instructionMFLO(r64& rd) -> void {
|
||||
rd.u64 = LO.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMTHI() -> void {
|
||||
HI.u64 = RS.u64;
|
||||
auto CPU::instructionMTHI(cr64& rs) -> void {
|
||||
HI.u64 = rs.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMTLO() -> void {
|
||||
LO.u64 = RS.u64;
|
||||
auto CPU::instructionMTLO(cr64& rs) -> void {
|
||||
LO.u64 = rs.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMULT() -> void {
|
||||
u64 result = i64(RS.i32) * i64(RT.i32);
|
||||
auto CPU::instructionMULT(cr64& rs, cr64& rt) -> 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);
|
||||
auto CPU::instructionMULTU(cr64& rs, cr64& rt) -> 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::instructionNOR(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = ~(rs.u64 | rt.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionOR() -> void {
|
||||
RD.u64 = RS.u64 | RT.u64;
|
||||
auto CPU::instructionOR(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 | rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionORI() -> void {
|
||||
RT.u64 = RS.u64 | IMMu16;
|
||||
auto CPU::instructionORI(r64& rt, cr64& rs, u16 imm) -> void {
|
||||
rt.u64 = rs.u64 | imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionSB() -> void {
|
||||
writeByte(RS.u32 + IMMi16, RT.u32);
|
||||
auto CPU::instructionSB(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeByte(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSC() -> void {
|
||||
auto CPU::instructionSC(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
RT.u64 = writeWord(RS.u32 + IMMi16, RT.u32);
|
||||
rt.u64 = writeWord(rs.u32 + imm, rt.u32);
|
||||
} else {
|
||||
RT.u64 = 0;
|
||||
rt.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSCD() -> void {
|
||||
auto CPU::instructionSCD(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
RT.u64 = writeDouble(RS.u32 + IMMi16, RT.u64);
|
||||
rt.u64 = writeDouble(rs.u32 + imm, rt.u64);
|
||||
} else {
|
||||
RT.u64 = 0;
|
||||
rt.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSD() -> void {
|
||||
writeDouble(RS.u32 + IMMi16, RT.u64);
|
||||
auto CPU::instructionSD(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeDouble(rs.u32 + imm, rt.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionSDL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionSDL(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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);
|
||||
writeDouble(address & ~7, *data & ~mask | rt.u64 >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSDR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionSDR(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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);
|
||||
writeDouble(address & ~7, *data & ~mask | rt.u64 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSH() -> void {
|
||||
writeHalf(RS.u32 + IMMi16, RT.u32);
|
||||
auto CPU::instructionSH(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeHalf(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLL() -> void {
|
||||
RD.u64 = i32(RT.u32 << SA);
|
||||
auto CPU::instructionSLL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = i32(rt.u32 << sa);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLLV() -> void {
|
||||
RD.u64 = i32(RT.u32 << (RS.u32 & 31));
|
||||
auto CPU::instructionSLLV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = i32(rt.u32 << (rs.u32 & 31));
|
||||
}
|
||||
|
||||
auto CPU::instructionSLT() -> void {
|
||||
RD.u64 = RS.i64 < RT.i64;
|
||||
auto CPU::instructionSLT(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.i64 < rt.i64;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTI() -> void {
|
||||
RT.u64 = RS.i64 < i64(IMMi16);
|
||||
auto CPU::instructionSLTI(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = rs.i64 < imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTIU() -> void {
|
||||
RT.u64 = RS.u64 < u64(IMMi16);
|
||||
auto CPU::instructionSLTIU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = rs.u64 < imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTU() -> void {
|
||||
RD.u64 = RS.u64 < RT.u64;
|
||||
auto CPU::instructionSLTU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 < rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionSRA() -> void {
|
||||
RD.u64 = RT.i32 >> SA;
|
||||
auto CPU::instructionSRA(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.i32 >> sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionSRAV() -> void {
|
||||
RD.u64 = RT.i32 >> (RS.u32 & 31);
|
||||
auto CPU::instructionSRAV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.i32 >> (rs.u32 & 31);
|
||||
}
|
||||
|
||||
auto CPU::instructionSRL() -> void {
|
||||
RD.u64 = i32(RT.u32 >> SA);
|
||||
auto CPU::instructionSRL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = i32(rt.u32 >> sa);
|
||||
}
|
||||
|
||||
auto CPU::instructionSRLV() -> void {
|
||||
RD.u64 = i32(RT.u32 >> (RS.u32 & 31));
|
||||
auto CPU::instructionSRLV(r64& rd, cr64& rt, cr64& rs) -> 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::instructionSUB(r64& rd, cr64& rs, cr64& rt) -> 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::instructionSUBU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = i32(rs.u32 - rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSW() -> void {
|
||||
writeWord(RS.u32 + IMMi16, RT.u32);
|
||||
auto CPU::instructionSW(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeWord(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSWL() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionSWL(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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);
|
||||
writeWord(address & ~3, *data & ~mask | rt.u32 >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSWR() -> void {
|
||||
auto address = RS.u32 + IMMi16;
|
||||
auto CPU::instructionSWR(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
auto address = rs.u32 + imm;
|
||||
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);
|
||||
writeWord(address & ~3, *data & ~mask | rt.u32 << shift);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSYNC() -> void {
|
||||
//no operation; for compatibility with R4000-series code
|
||||
}
|
||||
|
||||
auto CPU::instructionSYSCALL() -> void {
|
||||
exception.systemCall();
|
||||
}
|
||||
|
||||
auto CPU::instructionTEQ() -> void {
|
||||
if(RS.u64 == RT.u64) exception.trap();
|
||||
auto CPU::instructionTEQ(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.u64 == rt.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTEQI() -> void {
|
||||
if(RS.u64 == u64(IMMi16)) exception.trap();
|
||||
auto CPU::instructionTEQI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 == imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGE() -> void {
|
||||
if(RS.i64 >= RT.i64) exception.trap();
|
||||
auto CPU::instructionTGE(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.i64 >= rt.i64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEI() -> void {
|
||||
if(RS.i64 >= i64(IMMi16)) exception.trap();
|
||||
auto CPU::instructionTGEI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 >= imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEIU() -> void {
|
||||
if(RS.u64 >= u64(IMMi16)) exception.trap();
|
||||
auto CPU::instructionTGEIU(cr64& rs, i16 imm) -> void {
|
||||
if(rs.u64 >= imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEU() -> void {
|
||||
if(RS.u64 >= RT.u64) exception.trap();
|
||||
auto CPU::instructionTGEU(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.u64 >= rt.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLT() -> void {
|
||||
if(RS.i64 < RT.i64) exception.trap();
|
||||
auto CPU::instructionTLT(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.i64 < rt.i64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTI() -> void {
|
||||
if(RS.i64 < i64(IMMi16)) exception.trap();
|
||||
auto CPU::instructionTLTI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 < imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTIU() -> void {
|
||||
if(RS.u64 < u64(IMMi16)) exception.trap();
|
||||
auto CPU::instructionTLTIU(cr64& rs, i16 imm) -> void {
|
||||
if(rs.u64 < imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTU() -> void {
|
||||
if(RS.u64 < RT.u64) exception.trap();
|
||||
auto CPU::instructionTLTU(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.u64 < rt.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTNE() -> void {
|
||||
if(RS.u64 != RT.u64) exception.trap();
|
||||
auto CPU::instructionTNE(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.u64 != rt.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTNEI() -> void {
|
||||
if(RS.u64 != u64(IMMi16)) exception.trap();
|
||||
auto CPU::instructionTNEI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 != imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionXOR() -> void {
|
||||
RD.u64 = RS.u64 ^ RT.u64;
|
||||
auto CPU::instructionXOR(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 ^ rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionXORI() -> void {
|
||||
RT.u64 = RS.u64 ^ IMMu16;
|
||||
auto CPU::instructionXORI(r64& rt, cr64& rs, u16 imm) -> void {
|
||||
rt.u64 = rs.u64 ^ imm;
|
||||
}
|
||||
+115
-100
@@ -1,12 +1,15 @@
|
||||
//{
|
||||
union cpu64 {
|
||||
union r64 {
|
||||
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; };
|
||||
auto i128() const -> int128_t { return (int128_t)i64; }
|
||||
auto u128() const -> uint128_t { return (uint128_t)u64; }
|
||||
};
|
||||
using cr64 = const r64;
|
||||
|
||||
struct Core {
|
||||
enum Register : uint {
|
||||
@@ -24,10 +27,10 @@
|
||||
RA, //return address
|
||||
};
|
||||
|
||||
cpu64 r[32];
|
||||
cpu64 lo;
|
||||
cpu64 hi;
|
||||
u64 pc; //program counter
|
||||
r64 r[32];
|
||||
r64 lo;
|
||||
r64 hi;
|
||||
u64 pc; //program counter
|
||||
} core;
|
||||
|
||||
struct Branch {
|
||||
@@ -45,101 +48,113 @@
|
||||
} 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 instructionADD(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionADDI(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionADDIU(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionADDU(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionAND(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionANDI(r64& rt, cr64& rs, u16 imm) -> void;
|
||||
auto instructionBEQ(cr64& rs, cr64& rt, i16 imm) -> void;
|
||||
auto instructionBEQL(cr64& rs, cr64& rt, i16 imm) -> void;
|
||||
auto instructionBGEZ(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBGEZAL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBGEZALL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBGEZL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBGTZ(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBGTZL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBLEZ(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBLEZL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBLTZ(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBLTZAL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBLTZALL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBLTZL(cr64& rs, i16 imm) -> void;
|
||||
auto instructionBNE(cr64& rs, cr64& rt, i16 imm) -> void;
|
||||
auto instructionBNEL(cr64& rs, cr64& rt, i16 imm) -> void;
|
||||
auto instructionBAL(bool take, i16 imm) -> void;
|
||||
auto instructionBALL(bool take, i16 imm) -> 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 instructionCACHE(u8 cache, u8 operation) -> void;
|
||||
auto instructionDADD(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDADDI(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionDADDIU(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionDADDU(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDDIV(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDDIVU(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDIV(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDIVU(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDMULT(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDMULTU(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDSLL(r64& rd, cr64& rt, u8 sa) -> void;
|
||||
auto instructionDSLLV(r64& rd, cr64& rt, cr64& rs) -> void;
|
||||
auto instructionDSRA(r64& rd, cr64& rt, u8 sa) -> void;
|
||||
auto instructionDSRAV(r64& rd, cr64& rt, cr64& rs) -> void;
|
||||
auto instructionDSRL(r64& rd, cr64& rt, u8 sa) -> void;
|
||||
auto instructionDSRLV(r64& rd, cr64& rt, cr64& rs) -> void;
|
||||
auto instructionDSUB(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionDSUBU(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionJ(u32 imm) -> void;
|
||||
auto instructionJAL(u32 imm) -> void;
|
||||
auto instructionJALR(r64& rd, cr64& rs) -> void;
|
||||
auto instructionJR(cr64& rs) -> void;
|
||||
auto instructionLB(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLBU(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLD(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLDL(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLDR(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLH(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLHU(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLUI(r64& rt, u16 imm) -> void;
|
||||
auto instructionLL(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLLD(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLW(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLWL(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLWR(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLWU(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionMFHI(r64& rd) -> void;
|
||||
auto instructionMFLO(r64& rd) -> void;
|
||||
auto instructionMTHI(cr64& rs) -> void;
|
||||
auto instructionMTLO(cr64& rs) -> void;
|
||||
auto instructionMULT(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionMULTU(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionNOR(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionOR(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionORI(r64& rt, cr64& rs, u16 imm) -> void;
|
||||
auto instructionSB(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSC(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSD(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSCD(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSDL(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSDR(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSH(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSLL(r64& rd, cr64& rt, u8 sa) -> void;
|
||||
auto instructionSLLV(r64& rd, cr64& rt, cr64& rs) -> void;
|
||||
auto instructionSLT(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionSLTI(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSLTIU(r64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSLTU(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionSRA(r64& rd, cr64& rt, u8 sa) -> void;
|
||||
auto instructionSRAV(r64& rd, cr64& rt, cr64& rs) -> void;
|
||||
auto instructionSRL(r64& rd, cr64& rt, u8 sa) -> void;
|
||||
auto instructionSRLV(r64& rd, cr64& rt, cr64& rs) -> void;
|
||||
auto instructionSUB(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionSUBU(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionSW(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSWL(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSWR(cr64& rt, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSYNC() -> 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;
|
||||
auto instructionTEQ(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionTEQI(cr64& rs, i16 imm) -> void;
|
||||
auto instructionTGE(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionTGEI(cr64& rs, i16 imm) -> void;
|
||||
auto instructionTGEIU(cr64& rs, i16 imm) -> void;
|
||||
auto instructionTGEU(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionTLT(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionTLTI(cr64& rs, i16 imm) -> void;
|
||||
auto instructionTLTIU(cr64& rs, i16 imm) -> void;
|
||||
auto instructionTLTU(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionTNE(cr64& rs, cr64& rt) -> void;
|
||||
auto instructionTNEI(cr64& rs, i16 imm) -> void;
|
||||
auto instructionXOR(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto instructionXORI(r64& rt, cr64& rs, u16 imm) -> void;
|
||||
//};
|
||||
@@ -0,0 +1,66 @@
|
||||
#define OP pipeline.instruction
|
||||
#define RD GPR[RDn]
|
||||
#define RT GPR[RTn]
|
||||
#define RS GPR[RSn]
|
||||
|
||||
#define jp(id, name, ...) case id: return decoder##name(__VA_ARGS__)
|
||||
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__)
|
||||
#define br(id, name, ...) case id: return instruction##name(__VA_ARGS__)
|
||||
|
||||
auto CPU::decoderEXECUTE() -> void {
|
||||
#define DECODER_EXECUTE
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto CPU::decoderSPECIAL() -> void {
|
||||
#define DECODER_SPECIAL
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto CPU::decoderREGIMM() -> void {
|
||||
#define DECODER_REGIMM
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto CPU::decoderCOP0() -> void {
|
||||
if(!scc.status.enable.coprocessor0) {
|
||||
if(context.mode != Context::Mode::Kernel) return exception.coprocessor0();
|
||||
}
|
||||
|
||||
#define DECODER_COP0
|
||||
#include "decoder.hpp"
|
||||
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
|
||||
auto CPU::decoderCOP1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) {
|
||||
return exception.coprocessor1();
|
||||
}
|
||||
|
||||
#define DECODER_COP1
|
||||
#include "decoder.hpp"
|
||||
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
|
||||
auto CPU::instructionCOP2() -> void {
|
||||
exception.coprocessor2();
|
||||
}
|
||||
|
||||
auto CPU::instructionCOP3() -> void {
|
||||
exception.coprocessor3();
|
||||
}
|
||||
|
||||
auto CPU::instructionINVALID() -> void {
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
#undef jp
|
||||
#undef op
|
||||
#undef br
|
||||
|
||||
#undef OP
|
||||
#undef RD
|
||||
#undef RT
|
||||
#undef RS
|
||||
@@ -0,0 +1,353 @@
|
||||
//shares instruction decoding between interpreter and dynamic recompiler
|
||||
|
||||
#define SA (OP >> 6 & 31)
|
||||
#define RDn (OP >> 11 & 31)
|
||||
#define RTn (OP >> 16 & 31)
|
||||
#define RSn (OP >> 21 & 31)
|
||||
#define FD (OP >> 6 & 31)
|
||||
#define FS (OP >> 11 & 31)
|
||||
#define FT (OP >> 16 & 31)
|
||||
#define IMMi16 i16(OP)
|
||||
#define IMMu16 u16(OP)
|
||||
#define IMMu26 (OP & 0x03ff'ffff)
|
||||
|
||||
#ifdef DECODER_EXECUTE
|
||||
{
|
||||
switch(OP >> 26) {
|
||||
jp(0x00, SPECIAL);
|
||||
jp(0x01, REGIMM);
|
||||
br(0x02, J, IMMu26);
|
||||
br(0x03, JAL, IMMu26);
|
||||
br(0x04, BEQ, RS, RT, IMMi16);
|
||||
br(0x05, BNE, RS, RT, IMMi16);
|
||||
br(0x06, BLEZ, RS, IMMi16);
|
||||
br(0x07, BGTZ, RS, IMMi16);
|
||||
op(0x08, ADDI, RT, RS, IMMi16);
|
||||
op(0x09, ADDIU, RT, RS, IMMi16);
|
||||
op(0x0a, SLTI, RT, RS, IMMi16);
|
||||
op(0x0b, SLTIU, RT, RS, IMMi16);
|
||||
op(0x0c, ANDI, RT, RS, IMMu16);
|
||||
op(0x0d, ORI, RT, RS, IMMu16);
|
||||
op(0x0e, XORI, RT, RS, IMMu16);
|
||||
op(0x0f, LUI, RT, IMMu16);
|
||||
jp(0x10, COP0);
|
||||
jp(0x11, COP1);
|
||||
op(0x12, COP2);
|
||||
op(0x13, COP3);
|
||||
br(0x14, BEQL, RS, RT, IMMi16);
|
||||
br(0x15, BNEL, RS, RT, IMMi16);
|
||||
br(0x16, BLEZL, RS, IMMi16);
|
||||
br(0x17, BGTZL, RS, IMMi16);
|
||||
op(0x18, DADDI, RT, RS, IMMi16);
|
||||
op(0x19, DADDIU, RT, RS, IMMi16);
|
||||
op(0x1a, LDL, RT, RS, IMMi16);
|
||||
op(0x1b, LDR, RT, RS, IMMi16);
|
||||
op(0x1c, INVALID);
|
||||
op(0x1d, INVALID);
|
||||
op(0x1e, INVALID);
|
||||
op(0x1f, INVALID);
|
||||
op(0x20, LB, RT, RS, IMMi16);
|
||||
op(0x21, LH, RT, RS, IMMi16);
|
||||
op(0x22, LWL, RT, RS, IMMi16);
|
||||
op(0x23, LW, RT, RS, IMMi16);
|
||||
op(0x24, LBU, RT, RS, IMMi16);
|
||||
op(0x25, LHU, RT, RS, IMMi16);
|
||||
op(0x26, LWR, RT, RS, IMMi16);
|
||||
op(0x27, LWU, RT, RS, IMMi16);
|
||||
op(0x28, SB, RT, RS, IMMi16);
|
||||
op(0x29, SH, RT, RS, IMMi16);
|
||||
op(0x2a, SWL, RT, RS, IMMi16);
|
||||
op(0x2b, SW, RT, RS, IMMi16);
|
||||
op(0x2c, SDL, RT, RS, IMMi16);
|
||||
op(0x2d, SDR, RT, RS, IMMi16);
|
||||
op(0x2e, SWR, RT, RS, IMMi16);
|
||||
op(0x2f, CACHE, OP >> 16 & 3, OP >> 18 & 3);
|
||||
op(0x30, LL, RT, RS, IMMi16);
|
||||
op(0x31, LWC1, FT, RS, IMMi16);
|
||||
op(0x32, COP2); //LWC2
|
||||
op(0x33, COP3); //LWC3
|
||||
op(0x34, LLD, RT, RS, IMMi16);
|
||||
op(0x35, LDC1, FT, RS, IMMi16);
|
||||
op(0x36, COP2); //LDC2
|
||||
op(0x37, LD, RT, RS, IMMi16);
|
||||
op(0x38, SC, RT, RS, IMMi16);
|
||||
op(0x39, SWC1, FT, RS, IMMi16);
|
||||
op(0x3a, COP2); //SWC2
|
||||
op(0x3b, COP3); //SWC3
|
||||
op(0x3c, SCD, RT, RS, IMMi16);
|
||||
op(0x3d, SDC1, FT, RS, IMMi16);
|
||||
op(0x3e, COP2); //SDC2
|
||||
op(0x3f, SD, RT, RS, IMMi16);
|
||||
}
|
||||
}
|
||||
#undef DECODER_EXECUTE
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_SPECIAL
|
||||
{
|
||||
switch(OP & 0x3f) {
|
||||
op(0x00, SLL, RD, RT, SA);
|
||||
op(0x01, INVALID);
|
||||
op(0x02, SRL, RD, RT, SA);
|
||||
op(0x03, SRA, RD, RT, SA);
|
||||
op(0x04, SLLV, RD, RT, RS);
|
||||
op(0x05, INVALID);
|
||||
op(0x06, SRLV, RD, RT, RS);
|
||||
op(0x07, SRAV, RD, RT, RS);
|
||||
br(0x08, JR, RS);
|
||||
br(0x09, JALR, RD, RS);
|
||||
op(0x0a, INVALID);
|
||||
op(0x0b, INVALID);
|
||||
br(0x0c, SYSCALL);
|
||||
br(0x0d, BREAK);
|
||||
op(0x0e, INVALID);
|
||||
op(0x0f, SYNC);
|
||||
op(0x10, MFHI, RD);
|
||||
op(0x11, MTHI, RS);
|
||||
op(0x12, MFLO, RD);
|
||||
op(0x13, MTLO, RS);
|
||||
op(0x14, DSLLV, RD, RT, RS);
|
||||
op(0x15, INVALID);
|
||||
op(0x16, DSRLV, RD, RT, RS);
|
||||
op(0x17, DSRAV, RD, RT, RS);
|
||||
op(0x18, MULT, RS, RT);
|
||||
op(0x19, MULTU, RS, RT);
|
||||
op(0x1a, DIV, RS, RT);
|
||||
op(0x1b, DIVU, RS, RT);
|
||||
op(0x1c, DMULT, RS, RT);
|
||||
op(0x1d, DMULTU, RS, RT);
|
||||
op(0x1e, DDIV, RS, RT);
|
||||
op(0x1f, DDIVU, RS, RT);
|
||||
op(0x20, ADD, RD, RS, RT);
|
||||
op(0x21, ADDU, RD, RS, RT);
|
||||
op(0x22, SUB, RD, RS, RT);
|
||||
op(0x23, SUBU, RD, RS, RT);
|
||||
op(0x24, AND, RD, RS, RT);
|
||||
op(0x25, OR, RD, RS, RT);
|
||||
op(0x26, XOR, RD, RS, RT);
|
||||
op(0x27, NOR, RD, RS, RT);
|
||||
op(0x28, INVALID);
|
||||
op(0x29, INVALID);
|
||||
op(0x2a, SLT, RD, RS, RT);
|
||||
op(0x2b, SLTU, RD, RS, RT);
|
||||
op(0x2c, DADD, RD, RS, RT);
|
||||
op(0x2d, DADDU, RD, RS, RT);
|
||||
op(0x2e, DSUB, RD, RS, RT);
|
||||
op(0x2f, DSUBU, RD, RS, RT);
|
||||
op(0x30, TGE, RS, RT);
|
||||
op(0x31, TGEU, RS, RT);
|
||||
op(0x32, TLT, RS, RT);
|
||||
op(0x33, TLTU, RS, RT);
|
||||
op(0x34, TEQ, RS, RT);
|
||||
op(0x35, INVALID);
|
||||
op(0x36, TNE, RS, RT);
|
||||
op(0x37, INVALID);
|
||||
op(0x38, DSLL, RD, RT, SA);
|
||||
op(0x39, INVALID);
|
||||
op(0x3a, DSRL, RD, RT, SA);
|
||||
op(0x3b, DSRA, RD, RT, SA);
|
||||
op(0x3c, DSLL, RD, RT, SA + 32);
|
||||
op(0x3d, INVALID);
|
||||
op(0x3e, DSRL, RD, RT, SA + 32);
|
||||
op(0x3f, DSRA, RD, RT, SA + 32);
|
||||
}
|
||||
}
|
||||
#undef DECODER_SPECIAL
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_REGIMM
|
||||
{
|
||||
switch(OP >> 16 & 0x1f) {
|
||||
br(0x00, BLTZ, RS, IMMi16);
|
||||
br(0x01, BGEZ, RS, IMMi16);
|
||||
br(0x02, BLTZL, RS, IMMi16);
|
||||
br(0x03, BGEZL, RS, IMMi16);
|
||||
op(0x04, INVALID);
|
||||
op(0x05, INVALID);
|
||||
op(0x06, INVALID);
|
||||
op(0x07, INVALID);
|
||||
op(0x08, TGEI, RS, IMMi16);
|
||||
op(0x09, TGEIU, RS, IMMi16);
|
||||
op(0x0a, TLTI, RS, IMMi16);
|
||||
op(0x0b, TLTIU, RS, IMMi16);
|
||||
op(0x0c, TEQI, RS, IMMi16);
|
||||
op(0x0d, INVALID);
|
||||
op(0x0e, TNEI, RS, IMMi16);
|
||||
op(0x0f, INVALID);
|
||||
br(0x10, BLTZAL, RS, IMMi16);
|
||||
br(0x11, BGEZAL, RS, IMMi16);
|
||||
br(0x12, BLTZALL, RS, IMMi16);
|
||||
br(0x13, BGEZALL, RS, IMMi16);
|
||||
op(0x14, INVALID);
|
||||
op(0x15, INVALID);
|
||||
op(0x16, INVALID);
|
||||
op(0x17, INVALID);
|
||||
op(0x18, INVALID);
|
||||
op(0x19, INVALID);
|
||||
op(0x1a, INVALID);
|
||||
op(0x1b, INVALID);
|
||||
op(0x1c, INVALID);
|
||||
op(0x1d, INVALID);
|
||||
op(0x1e, INVALID);
|
||||
op(0x1f, INVALID);
|
||||
}
|
||||
}
|
||||
#undef DECODER_REGIMM
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_COP0
|
||||
{
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
op(0x00, MFC0, RT, RDn);
|
||||
op(0x01, DMFC0, RT, RDn);
|
||||
op(0x02, INVALID); //CFC0
|
||||
op(0x03, INVALID);
|
||||
op(0x04, MTC0, RT, RDn);
|
||||
op(0x05, DMTC0, RT, RDn);
|
||||
op(0x06, INVALID); //CTC0
|
||||
op(0x07, INVALID);
|
||||
op(0x08, INVALID); //BC0
|
||||
op(0x09, INVALID);
|
||||
op(0x0a, INVALID);
|
||||
op(0x0b, INVALID);
|
||||
op(0x0c, INVALID);
|
||||
op(0x0d, INVALID);
|
||||
op(0x0e, INVALID);
|
||||
op(0x0f, INVALID);
|
||||
}
|
||||
|
||||
switch(OP & 0x3f) {
|
||||
op(0x01, TLBR);
|
||||
op(0x02, TLBWI);
|
||||
op(0x06, TLBWR);
|
||||
op(0x08, TLBP);
|
||||
br(0x18, ERET);
|
||||
}
|
||||
}
|
||||
#undef DECODER_COP0
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_COP1
|
||||
{
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
op(0x00, MFC1, RT, FS);
|
||||
op(0x01, DMFC1, RT, FS);
|
||||
op(0x02, CFC1, RT, RDn);
|
||||
op(0x03, INVALID);
|
||||
op(0x04, MTC1, RT, FS);
|
||||
op(0x05, DMTC1, RT, FS);
|
||||
op(0x06, CTC1, RT, RDn);
|
||||
op(0x07, INVALID);
|
||||
br(0x08, BC1, OP >> 16 & 1, OP >> 17 & 1, IMMi16);
|
||||
op(0x09, INVALID);
|
||||
op(0x0a, INVALID);
|
||||
op(0x0b, INVALID);
|
||||
op(0x0c, INVALID);
|
||||
op(0x0d, INVALID);
|
||||
op(0x0e, INVALID);
|
||||
op(0x0f, INVALID);
|
||||
}
|
||||
|
||||
if((OP >> 21 & 31) == 16)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x00, FADD_S, FD, FS, FT);
|
||||
op(0x01, FSUB_S, FD, FS, FT);
|
||||
op(0x02, FMUL_S, FD, FS, FT);
|
||||
op(0x03, FDIV_S, FD, FS, FT);
|
||||
op(0x04, FSQRT_S, FD, FS);
|
||||
op(0x05, FABS_S, FD, FS);
|
||||
op(0x06, FMOV_S, FD, FS);
|
||||
op(0x07, FNEG_S, FD, FS);
|
||||
op(0x08, FROUND_L_S, FD, FS);
|
||||
op(0x09, FTRUNC_L_S, FD, FS);
|
||||
op(0x0a, FCEIL_L_S, FD, FS);
|
||||
op(0x0b, FFLOOR_L_S, FD, FS);
|
||||
op(0x0c, FROUND_W_S, FD, FS);
|
||||
op(0x0d, FTRUNC_W_S, FD, FS);
|
||||
op(0x0e, FCEIL_W_S, FD, FS);
|
||||
op(0x0f, FFLOOR_W_S, FD, FS);
|
||||
op(0x21, FCVT_D_S, FD, FS);
|
||||
op(0x24, FCVT_W_S, FD, FS);
|
||||
op(0x25, FCVT_L_S, FD, FS);
|
||||
op(0x30, FC_F_S, FS, FT);
|
||||
op(0x31, FC_UN_S, FS, FT);
|
||||
op(0x32, FC_EQ_S, FS, FT);
|
||||
op(0x33, FC_UEQ_S, FS, FT);
|
||||
op(0x34, FC_OLT_S, FS, FT);
|
||||
op(0x35, FC_ULT_S, FS, FT);
|
||||
op(0x36, FC_OLE_S, FS, FT);
|
||||
op(0x37, FC_ULE_S, FS, FT);
|
||||
op(0x38, FC_SF_S, FS, FT);
|
||||
op(0x39, FC_NGLE_S, FS, FT);
|
||||
op(0x3a, FC_SEQ_S, FS, FT);
|
||||
op(0x3b, FC_NGL_S, FS, FT);
|
||||
op(0x3c, FC_LT_S, FS, FT);
|
||||
op(0x3d, FC_NGE_S, FS, FT);
|
||||
op(0x3e, FC_LE_S, FS, FT);
|
||||
op(0x3f, FC_NGT_S, FS, FT);
|
||||
}
|
||||
|
||||
if((OP >> 21 & 31) == 17)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x00, FADD_D, FD, FS, FT);
|
||||
op(0x01, FSUB_D, FD, FS, FT);
|
||||
op(0x02, FMUL_D, FD, FS, FT);
|
||||
op(0x03, FDIV_D, FD, FS, FT);
|
||||
op(0x04, FSQRT_D, FD, FS);
|
||||
op(0x05, FABS_D, FD, FS);
|
||||
op(0x06, FMOV_D, FD, FS);
|
||||
op(0x07, FNEG_D, FD, FS);
|
||||
op(0x08, FROUND_L_D, FD, FS);
|
||||
op(0x09, FTRUNC_L_D, FD, FS);
|
||||
op(0x0a, FCEIL_L_D, FD, FS);
|
||||
op(0x0b, FFLOOR_L_D, FD, FS);
|
||||
op(0x0c, FROUND_W_D, FD, FS);
|
||||
op(0x0d, FTRUNC_W_D, FD, FS);
|
||||
op(0x0e, FCEIL_W_D, FD, FS);
|
||||
op(0x0f, FFLOOR_W_D, FD, FS);
|
||||
op(0x20, FCVT_S_D, FD, FS);
|
||||
op(0x24, FCVT_W_D, FD, FS);
|
||||
op(0x25, FCVT_L_D, FD, FS);
|
||||
op(0x30, FC_F_D, FS, FT);
|
||||
op(0x31, FC_UN_D, FS, FT);
|
||||
op(0x32, FC_EQ_D, FS, FT);
|
||||
op(0x33, FC_UEQ_D, FS, FT);
|
||||
op(0x34, FC_OLT_D, FS, FT);
|
||||
op(0x35, FC_ULT_D, FS, FT);
|
||||
op(0x36, FC_OLE_D, FS, FT);
|
||||
op(0x37, FC_ULE_D, FS, FT);
|
||||
op(0x38, FC_SF_D, FS, FT);
|
||||
op(0x39, FC_NGLE_D, FS, FT);
|
||||
op(0x3a, FC_SEQ_D, FS, FT);
|
||||
op(0x3b, FC_NGL_D, FS, FT);
|
||||
op(0x3c, FC_LT_D, FS, FT);
|
||||
op(0x3d, FC_NGE_D, FS, FT);
|
||||
op(0x3e, FC_LE_D, FS, FT);
|
||||
op(0x3f, FC_NGT_D, FS, FT);
|
||||
}
|
||||
|
||||
if((OP >> 21 & 31) == 20)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x20, FCVT_S_W, FD, FS);
|
||||
op(0x21, FCVT_D_W, FD, FS);
|
||||
}
|
||||
|
||||
if((OP >> 21 & 31) == 21)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x20, FCVT_S_L, FD, FS);
|
||||
op(0x21, FCVT_D_L, FD, FS);
|
||||
}
|
||||
}
|
||||
#undef DECODER_COP1
|
||||
#endif
|
||||
|
||||
#undef SA
|
||||
#undef RDn
|
||||
#undef RTn
|
||||
#undef RSn
|
||||
#undef FD
|
||||
#undef FS
|
||||
#undef FT
|
||||
#undef IMMi16
|
||||
#undef IMMu16
|
||||
#undef IMMu26
|
||||
@@ -379,16 +379,24 @@ auto CPU::Disassembler::FPU() -> vector<string> {
|
||||
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");
|
||||
case 0x24: return DS (s ? "cvtws" : "cvtwd" );
|
||||
case 0x25: return DS (s ? "cvtls" : "cvtld" );
|
||||
case 0x30: return ST(s ? "cfs" : "cfd" );
|
||||
case 0x31: return ST(s ? "cuns" : "cund" );
|
||||
case 0x32: return ST(s ? "ceqs" : "ceqd" );
|
||||
case 0x33: return ST(s ? "cueqs" : "cueqd" );
|
||||
case 0x34: return ST(s ? "colts" : "coltd" );
|
||||
case 0x35: return ST(s ? "cults" : "cultd" );
|
||||
case 0x36: return ST(s ? "coles" : "coled" );
|
||||
case 0x37: return ST(s ? "cules" : "culed" );
|
||||
case 0x38: return ST(s ? "csfs" : "csfd" );
|
||||
case 0x39: return ST(s ? "cngles" : "cngled");
|
||||
case 0x3a: return ST(s ? "cseqs" : "cseqd" );
|
||||
case 0x3b: return ST(s ? "cngls" : "cngld" );
|
||||
case 0x3c: return ST(s ? "clts" : "cltd" );
|
||||
case 0x3d: return ST(s ? "cnges" : "cnged" );
|
||||
case 0x3e: return ST(s ? "cles" : "cled" );
|
||||
case 0x3f: return ST(s ? "cngts" : "cngtd" );
|
||||
}
|
||||
|
||||
return {};
|
||||
@@ -437,7 +445,7 @@ auto CPU::Disassembler::sccRegisterName(uint index) const -> string {
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::sccRegisterValue(uint index) const -> string {
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index)), "}")};
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index), 8L), "}")};
|
||||
return sccRegisterName(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,300 +1,191 @@
|
||||
#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)
|
||||
#define FD(type) fgr<type>(fd)
|
||||
#define FS(type) fgr<type>(fs)
|
||||
#define FT(type) fgr<type>(ft)
|
||||
|
||||
auto CPU::instructionBC1() -> void {
|
||||
bool condition = OP >> 16 & 1;
|
||||
bool likely = OP >> 17 & 1;
|
||||
if(CF == condition) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
auto CPU::instructionBC1(bool value, bool likely, i16 imm) -> void {
|
||||
if(CF == value) branch.take(PC + 4 + (imm << 2));
|
||||
else if(likely) branch.discard();
|
||||
}
|
||||
|
||||
auto CPU::instructionCFC1() -> void {
|
||||
RT.u64 = i32(getControlRegisterFPU(RDn));
|
||||
auto CPU::instructionCFC1(r64& rt, u8 rd) -> void {
|
||||
rt.u64 = i32(getControlRegisterFPU(rd));
|
||||
}
|
||||
|
||||
auto CPU::instructionCTC1() -> void {
|
||||
setControlRegisterFPU(RDn, RT.u32);
|
||||
auto CPU::instructionCTC1(cr64& rt, u8 rd) -> void {
|
||||
setControlRegisterFPU(rd, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMFC1() -> void {
|
||||
RT.u64 = FS(u64);
|
||||
auto CPU::instructionDMFC1(r64& rt, u8 fs) -> void {
|
||||
rt.u64 = FS(u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMTC1() -> void {
|
||||
FS(u64) = RT.u64;
|
||||
auto CPU::instructionDMTC1(cr64& rt, u8 fs) -> 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::instructionFABS_S(u8 fd, u8 fs) -> void { FD(f32) = fabs(FS(f32)); }
|
||||
auto CPU::instructionFABS_D(u8 fd, u8 fs) -> void { FD(f64) = fabs(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFADD_S(u8 fd, u8 fs, u8 ft) -> void { FD(f32) = FS(f32) + FT(f32); }
|
||||
auto CPU::instructionFADD_D(u8 fd, u8 fs, u8 ft) -> void { FD(f64) = FS(f64) + FT(f64); }
|
||||
|
||||
auto CPU::instructionFCEIL_L_S(u8 fd, u8 fs) -> void { FD(i64) = ceil(FS(f32)); }
|
||||
auto CPU::instructionFCEIL_L_D(u8 fd, u8 fs) -> void { FD(i64) = ceil(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFCEIL_W_S(u8 fd, u8 fs) -> void { FD(i32) = ceil(FS(f32)); }
|
||||
auto CPU::instructionFCEIL_W_D(u8 fd, u8 fs) -> void { FD(i32) = ceil(FS(f64)); }
|
||||
|
||||
#define ORDERED(type, value) if(isnan(FS(type)) || isnan(FT(type))) { CF = value; return exception.floatingPoint(); }
|
||||
#define UNORDERED(type, value) if(isnan(FS(type)) || isnan(FT(type))) { CF = value; return; }
|
||||
|
||||
auto CPU::instructionFC_EQ_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = FS(f32) == FT(f32); }
|
||||
auto CPU::instructionFC_EQ_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = FS(f64) == FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_F_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = 0; }
|
||||
auto CPU::instructionFC_F_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = 0; }
|
||||
|
||||
auto CPU::instructionFC_LE_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = FS(f32) <= FT(f32); }
|
||||
auto CPU::instructionFC_LE_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = FS(f64) <= FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_LT_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = FS(f32) < FT(f32); }
|
||||
auto CPU::instructionFC_LT_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = FS(f64) < FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_NGE_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = FS(f32) < FT(f32); }
|
||||
auto CPU::instructionFC_NGE_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = FS(f64) < FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_NGL_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = FS(f32) == FT(f32); }
|
||||
auto CPU::instructionFC_NGL_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = FS(f64) == FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_NGLE_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = 0; }
|
||||
auto CPU::instructionFC_NGLE_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = 0; }
|
||||
|
||||
auto CPU::instructionFC_NGT_S(u8 fs, u8 ft) -> void { ORDERED(f32, 1); CF = FS(f32) <= FT(f32); }
|
||||
auto CPU::instructionFC_NGT_D(u8 fs, u8 ft) -> void { ORDERED(f64, 1); CF = FS(f64) <= FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_OLE_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = FS(f32) <= FT(f32); }
|
||||
auto CPU::instructionFC_OLE_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = FS(f64) <= FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_OLT_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 0); CF = FS(f32) < FT(f32); }
|
||||
auto CPU::instructionFC_OLT_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 0); CF = FS(f64) < FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_SEQ_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = FS(f32) == FT(f32); }
|
||||
auto CPU::instructionFC_SEQ_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = FS(f64) == FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_SF_S(u8 fs, u8 ft) -> void { ORDERED(f32, 0); CF = 0; }
|
||||
auto CPU::instructionFC_SF_D(u8 fs, u8 ft) -> void { ORDERED(f64, 0); CF = 0; }
|
||||
|
||||
auto CPU::instructionFC_UEQ_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = FS(f32) == FT(f32); }
|
||||
auto CPU::instructionFC_UEQ_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = FS(f64) == FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_ULE_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = FS(f32) <= FT(f32); }
|
||||
auto CPU::instructionFC_ULE_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = FS(f64) <= FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_ULT_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = FS(f32) < FT(f32); }
|
||||
auto CPU::instructionFC_ULT_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = FS(f64) < FT(f64); }
|
||||
|
||||
auto CPU::instructionFC_UN_S(u8 fs, u8 ft) -> void { UNORDERED(f32, 1); CF = 0; }
|
||||
auto CPU::instructionFC_UN_D(u8 fs, u8 ft) -> void { UNORDERED(f64, 1); CF = 0; }
|
||||
|
||||
#undef ORDERED
|
||||
#undef UNORDERED
|
||||
|
||||
auto CPU::instructionFCVT_S_D(u8 fd, u8 fs) -> void { FD(f32) = FS(f64); }
|
||||
auto CPU::instructionFCVT_S_W(u8 fd, u8 fs) -> void { FD(f32) = FS(i32); }
|
||||
auto CPU::instructionFCVT_S_L(u8 fd, u8 fs) -> void { FD(f32) = FS(i64); }
|
||||
|
||||
auto CPU::instructionFCVT_D_S(u8 fd, u8 fs) -> void { FD(f64) = FS(f32); }
|
||||
auto CPU::instructionFCVT_D_W(u8 fd, u8 fs) -> void { FD(f64) = FS(i32); }
|
||||
auto CPU::instructionFCVT_D_L(u8 fd, u8 fs) -> void { FD(f64) = FS(i64); }
|
||||
|
||||
auto CPU::instructionFCVT_L_S(u8 fd, u8 fs) -> void { FD(i64) = FS(f32); }
|
||||
auto CPU::instructionFCVT_L_D(u8 fd, u8 fs) -> void { FD(i64) = FS(f64); }
|
||||
|
||||
auto CPU::instructionFCVT_W_S(u8 fd, u8 fs) -> void { FD(i32) = FS(f32); }
|
||||
auto CPU::instructionFCVT_W_D(u8 fd, u8 fs) -> void { FD(i32) = FS(f64); }
|
||||
|
||||
auto CPU::instructionFDIV_S(u8 fd, u8 fs, u8 ft) -> void {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionFADD() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(f32) = FS(f32) + FT(f32);
|
||||
} else {
|
||||
FD(f64) = FS(f64) + FT(f64);
|
||||
auto CPU::instructionFDIV_D(u8 fd, u8 fs, u8 ft) -> void {
|
||||
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::instructionFCEILL() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i64) = ceil(FS(f32));
|
||||
} else {
|
||||
FD(i64) = ceil(FS(f64));
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFFLOOR_L_S(u8 fd, u8 fs) -> void { FD(i64) = floor(FS(f32)); }
|
||||
auto CPU::instructionFFLOOR_L_D(u8 fd, u8 fs) -> void { FD(i64) = floor(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFCEILW() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
FD(i32) = ceil(FS(f32));
|
||||
} else {
|
||||
FD(i32) = ceil(FS(f64));
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFFLOOR_W_S(u8 fd, u8 fs) -> void { FD(i32) = floor(FS(f32)); }
|
||||
auto CPU::instructionFFLOOR_W_D(u8 fd, u8 fs) -> void { FD(i32) = floor(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFCEQ() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) == FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) == FT(f64);
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFMOV_S(u8 fd, u8 fs) -> void { FD(f32) = FS(f32); }
|
||||
auto CPU::instructionFMOV_D(u8 fd, u8 fs) -> void { FD(f64) = FS(f64); }
|
||||
|
||||
auto CPU::instructionFCF() -> void {
|
||||
CF = 0;
|
||||
}
|
||||
auto CPU::instructionFMUL_S(u8 fd, u8 fs, u8 ft) -> void { FD(f32) = FS(f32) * FT(f32); }
|
||||
auto CPU::instructionFMUL_D(u8 fd, u8 fs, u8 ft) -> void { FD(f64) = FS(f64) * FT(f64); }
|
||||
|
||||
auto CPU::instructionFCOLE() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) <= FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) <= FT(f64);
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFNEG_S(u8 fd, u8 fs) -> void { FD(f32) = -FS(f32); }
|
||||
auto CPU::instructionFNEG_D(u8 fd, u8 fs) -> void { FD(f64) = -FS(f64); }
|
||||
|
||||
auto CPU::instructionFCOLT() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) < FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) < FT(f64);
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFROUND_L_S(u8 fd, u8 fs) -> void { FD(i64) = nearbyint(FS(f32)); }
|
||||
auto CPU::instructionFROUND_L_D(u8 fd, u8 fs) -> void { FD(i64) = nearbyint(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFCUEQ() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) == FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) == FT(f64);
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFROUND_W_S(u8 fd, u8 fs) -> void { FD(i32) = nearbyint(FS(f32)); }
|
||||
auto CPU::instructionFROUND_W_D(u8 fd, u8 fs) -> void { FD(i32) = nearbyint(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFCULE() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) <= FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) <= FT(f64);
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFSQRT_S(u8 fd, u8 fs) -> void { FD(f32) = sqrt(FS(f32)); }
|
||||
auto CPU::instructionFSQRT_D(u8 fd, u8 fs) -> void { FD(f64) = sqrt(FS(f64)); }
|
||||
|
||||
auto CPU::instructionFCULT() -> void {
|
||||
if(FPU_SINGLE) {
|
||||
CF = FS(f32) < FT(f32);
|
||||
} else {
|
||||
CF = FS(f64) < FT(f64);
|
||||
}
|
||||
}
|
||||
auto CPU::instructionFSUB_S(u8 fd, u8 fs, u8 ft) -> void { FD(f32) = FS(f32) - FT(f32); }
|
||||
auto CPU::instructionFSUB_D(u8 fd, u8 fs, u8 ft) -> void { FD(f64) = FS(f64) - FT(f64); }
|
||||
|
||||
auto CPU::instructionFCUN() -> void {
|
||||
CF = 0;
|
||||
}
|
||||
auto CPU::instructionFTRUNC_L_S(u8 fd, u8 fs) -> void { FD(i64) = FS(f32) < 0 ? ceil(FS(f32)) : floor(FS(f32)); }
|
||||
auto CPU::instructionFTRUNC_L_D(u8 fd, u8 fs) -> void { FD(i64) = FS(f64) < 0 ? ceil(FS(f64)) : floor(FS(f64)); }
|
||||
|
||||
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::instructionFTRUNC_W_S(u8 fd, u8 fs) -> void { FD(i32) = FS(f32) < 0 ? ceil(FS(f32)) : floor(FS(f32)); }
|
||||
auto CPU::instructionFTRUNC_W_D(u8 fd, u8 fs) -> void { FD(i32) = FS(f64) < 0 ? ceil(FS(f64)) : floor(FS(f64)); }
|
||||
|
||||
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 {
|
||||
auto CPU::instructionLDC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
if(auto data = readDouble(RS.u32 + IMMi16)) FT(u64) = *data;
|
||||
if(auto data = readDouble(rs.u32 + imm)) FT(u64) = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionLWC1() -> void {
|
||||
auto CPU::instructionLWC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
if(auto data = readWord(RS.u32 + IMMi16)) FT(u32) = *data;
|
||||
if(auto data = readWord(rs.u32 + imm)) FT(u32) = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionMFC1() -> void {
|
||||
RT.u64 = FS(i32);
|
||||
auto CPU::instructionMFC1(r64& rt, u8 fs) -> void {
|
||||
rt.u64 = FS(i32);
|
||||
}
|
||||
|
||||
auto CPU::instructionMTC1() -> void {
|
||||
FS(i32) = RT.u32;
|
||||
auto CPU::instructionMTC1(cr64& rt, u8 fs) -> void {
|
||||
FS(i32) = rt.u32;
|
||||
}
|
||||
|
||||
auto CPU::instructionSDC1() -> void {
|
||||
auto CPU::instructionSDC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
writeDouble(RS.u32 + IMMi16, FT(u64));
|
||||
writeDouble(rs.u32 + imm, FT(u64));
|
||||
}
|
||||
|
||||
auto CPU::instructionSWC1() -> void {
|
||||
auto CPU::instructionSWC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
writeWord(RS.u32 + IMMi16, FT(u32));
|
||||
writeWord(rs.u32 + imm, FT(u32));
|
||||
}
|
||||
|
||||
#undef CF
|
||||
#undef FD
|
||||
#undef FS
|
||||
#undef FT
|
||||
#undef FPU_SINGLE
|
||||
#undef FPU_DOUBLE
|
||||
#undef FPU_REAL
|
||||
#undef FPU_INTEGER
|
||||
@@ -1,3 +1,11 @@
|
||||
auto CPU::FPU::setFloatingPointMode(bool mode) -> void {
|
||||
if(mode == 0) {
|
||||
//32x64-bit -> 16x64-bit
|
||||
} else {
|
||||
//16x64-bit -> 32x64-bit
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<i32>(uint index) -> i32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].i32;
|
||||
|
||||
+89
-49
@@ -4,17 +4,11 @@
|
||||
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];
|
||||
//fpu-registers.cpp
|
||||
auto setFloatingPointMode(bool) -> void;
|
||||
|
||||
r64 r[32];
|
||||
|
||||
struct Coprocessor {
|
||||
static constexpr u8 revision = 0x00;
|
||||
@@ -51,43 +45,89 @@
|
||||
} 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;
|
||||
auto instructionBC1(bool value, bool likely, i16 imm) -> void;
|
||||
auto instructionCFC1(r64& rt, u8 rd) -> void;
|
||||
auto instructionCTC1(cr64& rt, u8 rd) -> void;
|
||||
auto instructionDMFC1(r64& rt, u8 fs) -> void;
|
||||
auto instructionDMTC1(cr64& rt, u8 fs) -> void;
|
||||
auto instructionFABS_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFABS_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFADD_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFADD_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFCEIL_L_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCEIL_L_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCEIL_W_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCEIL_W_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFC_EQ_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_EQ_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_F_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_F_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_LE_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_LE_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_LT_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_LT_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGE_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGE_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGL_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGL_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGLE_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGLE_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGT_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_NGT_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_OLE_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_OLE_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_OLT_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_OLT_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_SEQ_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_SEQ_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_SF_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_SF_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_UEQ_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_UEQ_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_ULE_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_ULE_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_ULT_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_ULT_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_UN_S(u8 fs, u8 ft) -> void;
|
||||
auto instructionFC_UN_D(u8 fs, u8 ft) -> void;
|
||||
auto instructionFCVT_S_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_S_W(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_S_L(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_D_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_D_W(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_D_L(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_L_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_L_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_W_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFCVT_W_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFDIV_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFDIV_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFFLOOR_L_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFFLOOR_L_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFFLOOR_W_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFFLOOR_W_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFMOV_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFMOV_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFMUL_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFMUL_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFNEG_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFNEG_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFROUND_L_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFROUND_L_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFROUND_W_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFROUND_W_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFSQRT_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFSQRT_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFSUB_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFSUB_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto instructionFTRUNC_L_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFTRUNC_L_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionFTRUNC_W_S(u8 fd, u8 fs) -> void;
|
||||
auto instructionFTRUNC_W_D(u8 fd, u8 fs) -> void;
|
||||
auto instructionLDC1(u8 ft, cr64& rs, i16 imm) -> void;
|
||||
auto instructionLWC1(u8 ft, cr64& rs, i16 imm) -> void;
|
||||
auto instructionMFC1(r64& rt, u8 fs) -> void;
|
||||
auto instructionMTC1(cr64& rt, u8 fs) -> void;
|
||||
auto instructionSDC1(u8 ft, cr64& rs, i16 imm) -> void;
|
||||
auto instructionSWC1(u8 ft, cr64& rs, i16 imm) -> void;
|
||||
//};
|
||||
@@ -1,317 +0,0 @@
|
||||
auto CPU::raiseException(uint code, uint coprocessor) -> void {
|
||||
if(debugger.tracer.exception->enabled()) {
|
||||
if(code != 0) debugger.exception(hex(code, 2L));
|
||||
}
|
||||
|
||||
if(!scc.status.exceptionLevel) {
|
||||
if(code == 10) print("* reserved instruction @ ", hex(pipeline.address), " ", hex(pipeline.instruction), "\n");
|
||||
scc.epc = PC;
|
||||
scc.status.exceptionLevel = 1;
|
||||
scc.cause.exceptionCode = code;
|
||||
scc.cause.coprocessorError = coprocessor;
|
||||
if(scc.cause.branchDelay = branch.inDelaySlot()) scc.epc -= 4;
|
||||
} else {
|
||||
scc.cause.exceptionCode = code;
|
||||
scc.cause.coprocessorError = coprocessor;
|
||||
}
|
||||
|
||||
u64 vectorBase = !scc.status.vectorLocation ? i32(0x8000'0000) : i32(0xbfc0'0200);
|
||||
u32 vectorOffset = (code == 2 || code == 3) ? 0x0000 : 0x0180;
|
||||
PC = vectorBase + vectorOffset;
|
||||
branch.exception();
|
||||
context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::instruction() -> void {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = readWord(pipeline.address)(0);
|
||||
|
||||
if(auto interrupts = scc.cause.interruptPending & scc.status.interruptMask) {
|
||||
if(scc.status.interruptEnable && !scc.status.exceptionLevel && !scc.status.errorLevel) {
|
||||
if(debugger.tracer.interrupt->enabled()) {
|
||||
debugger.interrupt(hex(scc.cause.interruptPending, 2L));
|
||||
}
|
||||
scc.cause.interruptPending = interrupts;
|
||||
return raiseException(0, interrupts);
|
||||
}
|
||||
}
|
||||
|
||||
debugger.instruction();
|
||||
//instructionDEBUG();
|
||||
instructionEXECUTE();
|
||||
GPR[0].u64 = 0;
|
||||
|
||||
switch(branch.state) {
|
||||
case Branch::Step: PC += 4; break;
|
||||
case Branch::DelaySlot: PC = branch.pc; branch.reset(); break;
|
||||
case Branch::Take: PC += 4; branch.delaySlot(); break;
|
||||
case Branch::Exception: branch.reset(); break;
|
||||
case Branch::Discard: PC += 8; branch.reset(); break;
|
||||
}
|
||||
|
||||
if(--scc.random.index < scc.wired.index) {
|
||||
scc.random.index = 31;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDEBUG() -> void {
|
||||
static vector<bool> mask;
|
||||
if(!mask) mask.resize(0x08000000);
|
||||
//if(mask[(PC & 0x1fffffff) >> 2]) return;
|
||||
mask[(PC & 0x1fffffff) >> 2] = 1;
|
||||
|
||||
static uint counter = 0;
|
||||
//if(++counter > 100) return;
|
||||
print(
|
||||
disassembler.hint(hex(pipeline.address, 8L)), " ",
|
||||
//disassembler.hint(hex(pipeline.instruction, 8L)), " ",
|
||||
disassembler.disassemble(pipeline.address, pipeline.instruction), "\n"
|
||||
);
|
||||
}
|
||||
|
||||
auto CPU::instructionEXECUTE() -> void {
|
||||
switch(OP >> 26) {
|
||||
case 0x00: return instructionSPECIAL();
|
||||
case 0x01: return instructionREGIMM();
|
||||
case 0x02: return instructionJ();
|
||||
case 0x03: return instructionJAL();
|
||||
case 0x04: return instructionB(RS.u64 == RT.u64); //BEQ
|
||||
case 0x05: return instructionB(RS.u64 != RT.u64); //BNE
|
||||
case 0x06: return instructionB(RS.i64 <= 0); //BLEZ
|
||||
case 0x07: return instructionB(RS.i64 > 0); //BGTZ
|
||||
case 0x08: return instructionADDI();
|
||||
case 0x09: return instructionADDIU();
|
||||
case 0x0a: return instructionSLTI();
|
||||
case 0x0b: return instructionSLTIU();
|
||||
case 0x0c: return instructionANDI();
|
||||
case 0x0d: return instructionORI();
|
||||
case 0x0e: return instructionXORI();
|
||||
case 0x0f: return instructionLUI();
|
||||
case 0x10: return instructionCOP0();
|
||||
case 0x11: return instructionCOP1();
|
||||
case 0x12: return exception.coprocessor2(); //COP2
|
||||
case 0x13: return exception.coprocessor3(); //COP3
|
||||
case 0x14: return instructionBL(RS.u64 == RT.u64); //BEQL
|
||||
case 0x15: return instructionBL(RS.u64 != RT.u64); //BNEL
|
||||
case 0x16: return instructionBL(RS.i64 <= 0); //BLEZL
|
||||
case 0x17: return instructionBL(RS.i64 > 0); //BGTZL
|
||||
case 0x18: return instructionDADDI();
|
||||
case 0x19: return instructionDADDIU();
|
||||
case 0x1a: return instructionLDL();
|
||||
case 0x1b: return instructionLDR();
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return instructionLB();
|
||||
case 0x21: return instructionLH();
|
||||
case 0x22: return instructionLWL();
|
||||
case 0x23: return instructionLW();
|
||||
case 0x24: return instructionLBU();
|
||||
case 0x25: return instructionLHU();
|
||||
case 0x26: return instructionLWR();
|
||||
case 0x27: return instructionLWU();
|
||||
case 0x28: return instructionSB();
|
||||
case 0x29: return instructionSH();
|
||||
case 0x2a: return instructionSWL();
|
||||
case 0x2b: return instructionSW();
|
||||
case 0x2c: return instructionSDL();
|
||||
case 0x2d: return instructionSDR();
|
||||
case 0x2e: return instructionSWR();
|
||||
case 0x2f: return instructionCACHE();
|
||||
case 0x30: return instructionLL();
|
||||
case 0x31: return instructionLWC1();
|
||||
case 0x32: return exception.coprocessor2(); //LWC2
|
||||
case 0x33: return exception.coprocessor3(); //LWC3
|
||||
case 0x34: return instructionLLD();
|
||||
case 0x35: return instructionLDC1();
|
||||
case 0x36: return exception.coprocessor2(); //LDC2
|
||||
case 0x37: return instructionLD();
|
||||
case 0x38: return instructionSC();
|
||||
case 0x39: return instructionSWC1();
|
||||
case 0x3a: return exception.coprocessor2(); //SWC2
|
||||
case 0x3b: return exception.coprocessor3(); //SWC3
|
||||
case 0x3c: return instructionSCD();
|
||||
case 0x3d: return instructionSDC1();
|
||||
case 0x3e: return exception.coprocessor2(); //SDC2
|
||||
case 0x3f: return instructionSD();
|
||||
}
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionSPECIAL() -> void {
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionSLL();
|
||||
case 0x01: break;
|
||||
case 0x02: return instructionSRL();
|
||||
case 0x03: return instructionSRA();
|
||||
case 0x04: return instructionSLLV();
|
||||
case 0x05: break;
|
||||
case 0x06: return instructionSRLV();
|
||||
case 0x07: return instructionSRAV();
|
||||
case 0x08: return instructionJR();
|
||||
case 0x09: return instructionJALR();
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: return instructionSYSCALL();
|
||||
case 0x0d: return instructionBREAK();
|
||||
case 0x0e: break;
|
||||
case 0x0f: return; //SYNC
|
||||
case 0x10: return instructionMFHI();
|
||||
case 0x11: return instructionMTHI();
|
||||
case 0x12: return instructionMFLO();
|
||||
case 0x13: return instructionMTLO();
|
||||
case 0x14: return instructionDSLLV();
|
||||
case 0x15: break;
|
||||
case 0x16: return instructionDSRLV();
|
||||
case 0x17: return instructionDSRAV();
|
||||
case 0x18: return instructionMULT();
|
||||
case 0x19: return instructionMULTU();
|
||||
case 0x1a: return instructionDIV();
|
||||
case 0x1b: return instructionDIVU();
|
||||
case 0x1c: return instructionDMULT();
|
||||
case 0x1d: return instructionDMULTU();
|
||||
case 0x1e: return instructionDDIV();
|
||||
case 0x1f: return instructionDDIVU();
|
||||
case 0x20: return instructionADD();
|
||||
case 0x21: return instructionADDU();
|
||||
case 0x22: return instructionSUB();
|
||||
case 0x23: return instructionSUBU();
|
||||
case 0x24: return instructionAND();
|
||||
case 0x25: return instructionOR();
|
||||
case 0x26: return instructionXOR();
|
||||
case 0x27: return instructionNOR();
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return instructionSLT();
|
||||
case 0x2b: return instructionSLTU();
|
||||
case 0x2c: return instructionDADD();
|
||||
case 0x2d: return instructionDADDU();
|
||||
case 0x2e: return instructionDSUB();
|
||||
case 0x2f: return instructionDSUBU();
|
||||
case 0x30: return instructionTGE();
|
||||
case 0x31: return instructionTGEU();
|
||||
case 0x32: return instructionTLT();
|
||||
case 0x33: return instructionTLTU();
|
||||
case 0x34: return instructionTEQ();
|
||||
case 0x35: break;
|
||||
case 0x36: return instructionTNE();
|
||||
case 0x37: break;
|
||||
case 0x38: return instructionDSLL();
|
||||
case 0x39: break;
|
||||
case 0x3a: return instructionDSRL();
|
||||
case 0x3b: return instructionDSRA();
|
||||
case 0x3c: return instructionDSLL32();
|
||||
case 0x3d: break;
|
||||
case 0x3e: return instructionDSRL32();
|
||||
case 0x3f: return instructionDSRA32();
|
||||
}
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionREGIMM() -> void {
|
||||
switch(OP >> 16 & 0x1f) {
|
||||
case 0x00: return instructionB (RS.i64 < 0); //BLTZ
|
||||
case 0x01: return instructionB (RS.i64 >= 0); //BGEZ
|
||||
case 0x02: return instructionBL(RS.i64 < 0); //BLTZL
|
||||
case 0x03: return instructionBL(RS.i64 >= 0); //BGEZL
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: return instructionTGEI();
|
||||
case 0x09: return instructionTGEIU();
|
||||
case 0x0a: return instructionTLTI();
|
||||
case 0x0b: return instructionTLTIU();
|
||||
case 0x0c: return instructionTEQI();
|
||||
case 0x0d: break;
|
||||
case 0x0e: return instructionTNEI();
|
||||
case 0x0f: break;
|
||||
case 0x10: return instructionBAL (RS.i64 < 0); //BLTZAL
|
||||
case 0x11: return instructionBAL (RS.i64 >= 0); //BGEZAL
|
||||
case 0x12: return instructionBALL(RS.i64 < 0); //BLTZALL
|
||||
case 0x13: return instructionBALL(RS.i64 >= 0); //BGEZALL
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::instructionCOP0() -> void {
|
||||
if(!scc.status.enable.coprocessor0) {
|
||||
if(context.mode != Context::Mode::Kernel) return exception.coprocessor0();
|
||||
}
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
case 0x00: return instructionMFC0();
|
||||
case 0x01: return instructionDMFC0();
|
||||
case 0x02: break; //CFC0
|
||||
case 0x04: return instructionMTC0();
|
||||
case 0x05: return instructionDMTC0();
|
||||
case 0x06: break; //CTC0
|
||||
case 0x08: break; //BC0F, BC0FL, BC0T, BC0TL
|
||||
}
|
||||
if(!(OP >> 25 & 1)) return exception.reservedInstruction();
|
||||
switch(OP & 0x3f) {
|
||||
case 0x01: return instructionTLBR();
|
||||
case 0x02: return instructionTLBWI();
|
||||
case 0x06: return instructionTLBWR();
|
||||
case 0x08: return instructionTLBP();
|
||||
case 0x18: return instructionERET();
|
||||
}
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
|
||||
auto CPU::instructionCOP1() -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
case 0x00: return instructionMFC1();
|
||||
case 0x01: return instructionDMFC1();
|
||||
case 0x02: return instructionCFC1();
|
||||
case 0x04: return instructionMTC1();
|
||||
case 0x05: return instructionDMTC1();
|
||||
case 0x06: return instructionCTC1();
|
||||
case 0x08: return instructionBC1();
|
||||
}
|
||||
if(!(OP >> 25 & 1)) return exception.reservedInstruction();
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionFADD();
|
||||
case 0x01: return instructionFSUB();
|
||||
case 0x02: return instructionFMUL();
|
||||
case 0x03: return instructionFDIV();
|
||||
case 0x04: return instructionFSQRT();
|
||||
case 0x05: return instructionFABS();
|
||||
case 0x06: return instructionFMOV();
|
||||
case 0x07: return instructionFNEG();
|
||||
case 0x08: return instructionFROUNDL();
|
||||
case 0x09: return instructionFTRUNCL();
|
||||
case 0x0a: return instructionFCEILL();
|
||||
case 0x0b: return instructionFFLOORL();
|
||||
case 0x0c: return instructionFROUNDW();
|
||||
case 0x0d: return instructionFTRUNCW();
|
||||
case 0x0e: return instructionFCEILW();
|
||||
case 0x0f: return instructionFFLOORW();
|
||||
case 0x20: return instructionFCVTS();
|
||||
case 0x21: return instructionFCVTD();
|
||||
case 0x24: return instructionFCVTW();
|
||||
case 0x25: return instructionFCVTL();
|
||||
case 0x30: case 0x38: return instructionFCF();
|
||||
case 0x31: case 0x39: return instructionFCUN();
|
||||
case 0x32: case 0x3a: return instructionFCEQ();
|
||||
case 0x33: case 0x3b: return instructionFCUEQ();
|
||||
case 0x34: case 0x3c: return instructionFCOLT();
|
||||
case 0x35: case 0x3d: return instructionFCULT();
|
||||
case 0x36: case 0x3e: return instructionFCOLE();
|
||||
case 0x37: case 0x3f: return instructionFCULE();
|
||||
}
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
auto CPU::Recompiler::pool(u32 address) -> Pool* {
|
||||
auto& pool = pools[address >> 8 & 0x1fffff];
|
||||
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
return pool;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::block(u32 address) -> Block* {
|
||||
auto& block = pool(address)->blocks[address >> 2 & 0x3f];
|
||||
if(!block) block = emit(address);
|
||||
return block;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emit(u32 address) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("CPU allocator flush\n");
|
||||
allocator.release(bump_allocator::zero_fill);
|
||||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
block->code = allocator.acquire();
|
||||
bind({block->code, allocator.available()});
|
||||
|
||||
bool branched = 0;
|
||||
uint instructions = 0;
|
||||
uint idle = 0;
|
||||
do {
|
||||
u32 instruction = bus.readWord(address);
|
||||
if(instruction == 0x1000'ffff) idle += 64; //accelerate idle loops
|
||||
branched = emitEXECUTE(instruction);
|
||||
call(&CPU::instructionEpilogue, &self);
|
||||
test(rax, rax);
|
||||
jz(imm8{+1});
|
||||
ret();
|
||||
address += 4;
|
||||
if((address & 0xfc) == 0) break; //block boundary
|
||||
} while(++instructions < 64 && !branched);
|
||||
ret();
|
||||
|
||||
block->step = 2 * instructions + idle;
|
||||
allocator.reserve(size());
|
||||
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
|
||||
return block;
|
||||
}
|
||||
|
||||
#define OP instruction
|
||||
#define RD &self.GPR[RDn]
|
||||
#define RT &self.GPR[RTn]
|
||||
#define RS &self.GPR[RSn]
|
||||
|
||||
#define jp(id, name) \
|
||||
case id: \
|
||||
return emit##name(instruction); \
|
||||
|
||||
#define op(id, name, ...) \
|
||||
case id: \
|
||||
call(&CPU::instruction##name, &self, ##__VA_ARGS__); \
|
||||
return 0; \
|
||||
|
||||
#define br(id, name, ...) \
|
||||
case id: \
|
||||
call(&CPU::instruction##name, &self, ##__VA_ARGS__); \
|
||||
return 1; \
|
||||
|
||||
auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
||||
#define DECODER_EXECUTE
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
||||
#define DECODER_SPECIAL
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emitREGIMM(u32 instruction) -> bool {
|
||||
#define DECODER_REGIMM
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emitCOP0(u32 instruction) -> bool {
|
||||
#define DECODER_COP0
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emitCOP1(u32 instruction) -> bool {
|
||||
call(&CPU::Recompiler::emitCheckCOP1, this);
|
||||
test(rax, rax);
|
||||
jz(imm8{+1});
|
||||
ret();
|
||||
|
||||
#define DECODER_COP1
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emitCheckCOP1() -> bool {
|
||||
if(!self.scc.status.enable.coprocessor1) {
|
||||
return self.exception.coprocessor1(), true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef jp
|
||||
#undef op
|
||||
#undef br
|
||||
|
||||
#undef OP
|
||||
#undef RD
|
||||
#undef RT
|
||||
#undef RS
|
||||
@@ -0,0 +1,41 @@
|
||||
//{
|
||||
struct Recompiler : recompiler::amd64 {
|
||||
CPU& self;
|
||||
Recompiler(CPU& self) : self(self) {}
|
||||
|
||||
struct Block {
|
||||
auto execute() -> void {
|
||||
((void (*)())code)();
|
||||
}
|
||||
|
||||
u32 step;
|
||||
u8* code;
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
Block* blocks[1 << 6];
|
||||
};
|
||||
|
||||
auto reset() -> void {
|
||||
for(u32 index : range(1 << 21)) pools[index] = nullptr;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address) -> void {
|
||||
pools[address >> 8 & 0x1fffff] = nullptr;
|
||||
}
|
||||
|
||||
auto pool(u32 address) -> Pool*;
|
||||
auto block(u32 address) -> Block*;
|
||||
|
||||
auto emit(u32 address) -> Block*;
|
||||
auto emitEXECUTE(u32 instruction) -> bool;
|
||||
auto emitSPECIAL(u32 instruction) -> bool;
|
||||
auto emitREGIMM(u32 instruction) -> bool;
|
||||
auto emitCOP0(u32 instruction) -> bool;
|
||||
auto emitCOP1(u32 instruction) -> bool;
|
||||
auto emitCheckCOP1() -> bool;
|
||||
|
||||
bump_allocator allocator;
|
||||
Pool* pools[1 << 21]; //2_MiB * sizeof(void*) == 16_MiB
|
||||
} recompiler{*this};
|
||||
//};
|
||||
@@ -1,47 +1,20 @@
|
||||
#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
|
||||
@@ -1,9 +1,9 @@
|
||||
auto CPU::instructionDMFC0() -> void {
|
||||
RT.u64 = getControlRegister(RDn);
|
||||
auto CPU::instructionDMFC0(r64& rt, u8 rd) -> void {
|
||||
rt.u64 = getControlRegister(rd);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMTC0() -> void {
|
||||
setControlRegister(RDn, RT.u64);
|
||||
auto CPU::instructionDMTC0(cr64& rt, u8 rd) -> void {
|
||||
setControlRegister(rd, rt.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionERET() -> void {
|
||||
@@ -19,12 +19,12 @@ auto CPU::instructionERET() -> void {
|
||||
context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::instructionMFC0() -> void {
|
||||
RT.u64 = i32(getControlRegister(RDn));
|
||||
auto CPU::instructionMFC0(r64& rt, u8 rd) -> void {
|
||||
rt.u64 = i32(getControlRegister(rd));
|
||||
}
|
||||
|
||||
auto CPU::instructionMTC0() -> void {
|
||||
setControlRegister(RDn, i32(RT.u32));
|
||||
auto CPU::instructionMTC0(cr64& rt, u8 rd) -> void {
|
||||
setControlRegister(rd, i32(rt.u32));
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBP() -> void {
|
||||
|
||||
@@ -181,7 +181,8 @@ auto CPU::setControlRegister(uint5 index, uint64 data) -> void {
|
||||
scc.compare = data.bit(0,31);
|
||||
scc.cause.interruptPending.bit(Interrupt::Timer) = 0;
|
||||
break;
|
||||
case 12: //status
|
||||
case 12: {//status
|
||||
bool floatingPointMode = scc.status.floatingPointMode;
|
||||
scc.status.interruptEnable = data.bit( 0);
|
||||
scc.status.exceptionLevel = data.bit( 1);
|
||||
scc.status.errorLevel = data.bit( 2);
|
||||
@@ -204,7 +205,10 @@ auto CPU::setControlRegister(uint5 index, uint64 data) -> void {
|
||||
scc.status.enable.coprocessor1 = data.bit(29);
|
||||
scc.status.enable.coprocessor2 = data.bit(30);
|
||||
scc.status.enable.coprocessor3 = data.bit(31);
|
||||
break;
|
||||
if(floatingPointMode != scc.status.floatingPointMode) {
|
||||
fpu.setFloatingPointMode(scc.status.floatingPointMode);
|
||||
}
|
||||
} break;
|
||||
case 13: //cause
|
||||
scc.cause.interruptPending.bit(0) = data.bit(8);
|
||||
scc.cause.interruptPending.bit(1) = data.bit(9);
|
||||
|
||||
@@ -23,7 +23,7 @@ auto CPU::TLB::store(u32 address) -> maybe<u32> {
|
||||
|
||||
auto CPU::TLB::exception(u32 address) -> void {
|
||||
self.scc.badVirtualAddress = address;
|
||||
self.scc.tlb.addressSpaceID = address >> 32; //note: needs u64 address
|
||||
self.scc.tlb.addressSpaceID = 0; //address >> 32; //note: needs u64 address
|
||||
self.scc.tlb.virtualAddress = address >> 13;
|
||||
self.scc.context.badVirtualAddress = address >> 13;
|
||||
self.scc.xcontext.badVirtualAddress = address >> 13;
|
||||
|
||||
@@ -174,11 +174,11 @@
|
||||
} scc;
|
||||
|
||||
//scc-instructions.cpp
|
||||
auto instructionDMFC0() -> void;
|
||||
auto instructionDMTC0() -> void;
|
||||
auto instructionDMFC0(r64& rt, u8 rd) -> void;
|
||||
auto instructionDMTC0(cr64& rt, u8 rd) -> void;
|
||||
auto instructionERET() -> void;
|
||||
auto instructionMFC0() -> void;
|
||||
auto instructionMTC0() -> void;
|
||||
auto instructionMFC0(r64& rt, u8 rd) -> void;
|
||||
auto instructionMTC0(cr64& rt, u8 rd) -> void;
|
||||
auto instructionTLBP() -> void;
|
||||
auto instructionTLBR() -> void;
|
||||
auto instructionTLBWI() -> void;
|
||||
|
||||
@@ -19,7 +19,6 @@ auto CPU::unload() -> void {
|
||||
|
||||
auto CPU::main() -> void {
|
||||
instruction();
|
||||
step(2);
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
@@ -32,11 +31,10 @@ auto CPU::step(uint clocks) -> void {
|
||||
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;
|
||||
}
|
||||
if(scc.count < scc.compare && scc.count + clocks >= scc.compare) {
|
||||
scc.cause.interruptPending.bit(Interrupt::Timer) = 1;
|
||||
}
|
||||
scc.count += clocks;
|
||||
}
|
||||
|
||||
auto CPU::power(bool reset) -> void {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
DD dd;
|
||||
#include "io.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto DD::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("Disk Drive");
|
||||
c2s.allocate(0x400);
|
||||
ds.allocate(0x100);
|
||||
ms.allocate(0x40);
|
||||
}
|
||||
|
||||
auto DD::unload() -> void {
|
||||
c2s.reset();
|
||||
ds.reset();
|
||||
ms.reset();
|
||||
node.reset();
|
||||
iplrom.reset();
|
||||
}
|
||||
|
||||
auto DD::power(bool reset) -> void {
|
||||
iplrom.allocate(4_MiB);
|
||||
if(auto fp = platform->open(node, "64dd.ipl.rom", File::Read)) {
|
||||
iplrom.load(fp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//Disk Drive
|
||||
|
||||
struct DD : Memory::IO<DD> {
|
||||
Node::Component node;
|
||||
Memory::Readable iplrom;
|
||||
Memory::Writable c2s;
|
||||
Memory::Writable ds;
|
||||
Memory::Writable ms;
|
||||
|
||||
//dd.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern DD dd;
|
||||
@@ -0,0 +1,8 @@
|
||||
auto DD::readWord(u32 address) -> u32 {
|
||||
uint32 data;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto DD::writeWord(u32 address, u32 data) -> void {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
auto DD::serialize(serializer& s) -> void {
|
||||
}
|
||||
+54
-200
@@ -1,231 +1,85 @@
|
||||
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;
|
||||
};
|
||||
#define decode(write, access, ...) \
|
||||
if(address <= 0x007f'ffff) return rdram.ram.access(__VA_ARGS__); \
|
||||
if(address <= 0x03ef'ffff) return unmapped; \
|
||||
if(address <= 0x03ff'ffff) return rdram.access(__VA_ARGS__); \
|
||||
if(address <= 0x0400'0fff) return rsp.dmem.access(__VA_ARGS__); \
|
||||
if(address <= 0x0400'1fff) { \
|
||||
if constexpr(write) { rsp.recompiler.invalidate(); } \
|
||||
return rsp.imem.access(__VA_ARGS__); \
|
||||
} \
|
||||
if(address <= 0x0403'ffff) return unmapped; \
|
||||
if(address <= 0x0407'ffff) return rsp.access(__VA_ARGS__); \
|
||||
if(address <= 0x040f'ffff) return rsp.status.access(__VA_ARGS__); \
|
||||
if(address <= 0x041f'ffff) return rdp.access(__VA_ARGS__); \
|
||||
if(address <= 0x042f'ffff) return rdp.io.access(__VA_ARGS__); \
|
||||
if(address <= 0x043f'ffff) return mi.access(__VA_ARGS__); \
|
||||
if(address <= 0x044f'ffff) return vi.access(__VA_ARGS__); \
|
||||
if(address <= 0x045f'ffff) return ai.access(__VA_ARGS__); \
|
||||
if(address <= 0x046f'ffff) return pi.access(__VA_ARGS__); \
|
||||
if(address <= 0x047f'ffff) return ri.access(__VA_ARGS__); \
|
||||
if(address <= 0x048f'ffff) return si.access(__VA_ARGS__); \
|
||||
if(address <= 0x04ff'ffff) return unmapped; \
|
||||
if(address <= 0x0500'03ff) return dd.c2s.access(__VA_ARGS__); \
|
||||
if(address <= 0x0500'04ff) return dd.ds.access(__VA_ARGS__); \
|
||||
if(address <= 0x0500'057f) return dd.access(__VA_ARGS__); \
|
||||
if(address <= 0x0500'05bf) return dd.ms.access(__VA_ARGS__); \
|
||||
if(address <= 0x05ff'ffff) return unmapped; \
|
||||
if(address <= 0x063f'ffff) return dd.iplrom.access(__VA_ARGS__); \
|
||||
if(address <= 0x0fff'ffff) return unmapped; \
|
||||
if(address <= 0x1fbf'ffff) return cartridge.rom.access(__VA_ARGS__); \
|
||||
if(address <= 0x1fc0'07bf) return pi.rom.access(__VA_ARGS__); \
|
||||
if(address <= 0x1fc0'07ff) return pi.ram.access(__VA_ARGS__); \
|
||||
return unmapped; \
|
||||
|
||||
#define unmapped 0
|
||||
|
||||
inline auto Bus::readByte(u32 address) -> u8 {
|
||||
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;
|
||||
decode(0, readByte, address);
|
||||
}
|
||||
|
||||
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;
|
||||
decode(0, readHalf, address);
|
||||
}
|
||||
|
||||
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;
|
||||
decode(0, readWord, address);
|
||||
}
|
||||
|
||||
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;
|
||||
decode(0, readDouble, address);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
#undef unmapped
|
||||
#define unmapped
|
||||
|
||||
inline auto Bus::writeByte(u32 address, u8 data) -> void {
|
||||
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;
|
||||
cpu.recompiler.invalidate(address);
|
||||
decode(1, writeByte, address, data);
|
||||
}
|
||||
|
||||
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;
|
||||
cpu.recompiler.invalidate(address);
|
||||
decode(1, writeHalf, address, data);
|
||||
}
|
||||
|
||||
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;
|
||||
cpu.recompiler.invalidate(address);
|
||||
decode(1, writeWord, address, data);
|
||||
}
|
||||
|
||||
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;
|
||||
cpu.recompiler.invalidate(address + 0);
|
||||
cpu.recompiler.invalidate(address + 4);
|
||||
decode(2, writeDouble, address, data);
|
||||
}
|
||||
|
||||
#undef unmapped
|
||||
#undef decode
|
||||
+124
-21
@@ -1,13 +1,6 @@
|
||||
//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; }
|
||||
namespace Memory {
|
||||
|
||||
struct Readable {
|
||||
auto reset() -> void {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
@@ -31,14 +24,84 @@ struct Memory {
|
||||
}
|
||||
|
||||
auto fill(u32 value = 0) -> void {
|
||||
for(uint address = 0; address < size; address += 4) {
|
||||
for(u32 address = 0; address < size; address += 4) {
|
||||
*(u32*)&data[address & maskWord] = bswap32(value);
|
||||
}
|
||||
}
|
||||
|
||||
auto load(Shared::File fp) -> void {
|
||||
for(u32 address = 0; address < min(size, fp->size()); address += 4) {
|
||||
*(u32*)&data[address & maskWord] = bswap32(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 {}
|
||||
auto writeHalf(u32 address, u16 value) -> void {}
|
||||
auto writeWord(u32 address, u32 value) -> void {}
|
||||
auto writeDouble(u32 address, u64 value) -> void {}
|
||||
auto writeQuad(u32 address, u128 value) -> void {}
|
||||
|
||||
//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 {}
|
||||
auto writeWordUnaligned(u32 address, u32 value) -> void {}
|
||||
auto writeDoubleUnaligned(u32 address, u64 value) -> void {}
|
||||
auto writeQuadUnaligned(u32 address, u128 value) -> void {}
|
||||
|
||||
//private:
|
||||
u8* data = nullptr;
|
||||
u32 size = 0;
|
||||
u32 maskByte = 0;
|
||||
u32 maskHalf = 0;
|
||||
u32 maskWord = 0;
|
||||
u32 maskDouble = 0;
|
||||
u32 maskQuad = 0;
|
||||
};
|
||||
|
||||
struct Writable {
|
||||
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(u32 address = 0; address < size; address += 4) {
|
||||
*(u32*)&data[address & maskWord] = bswap32(value);
|
||||
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));
|
||||
for(u32 address = 0; address < min(size, fp->size()); address += 4) {
|
||||
*(u32*)&data[address & maskWord] = bswap32(fp->readm(4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,13 +139,57 @@ struct Memory {
|
||||
u32 maskQuad = 0;
|
||||
};
|
||||
|
||||
//00000000-7fffffff kuseg
|
||||
//80000000-9fffffff kseg0
|
||||
//a0000000-bfffffff kseg1
|
||||
//c0000000-dfffffff ksseg
|
||||
//e0000000-ffffffff kseg3
|
||||
template<typename T>
|
||||
struct IO {
|
||||
auto readByte(u32 address) -> u8 {
|
||||
auto data = ((T*)this)->readWord(address);
|
||||
switch(address & 3) {
|
||||
case 0: return data >> 24;
|
||||
case 1: return data >> 16;
|
||||
case 2: return data >> 8;
|
||||
case 3: return data >> 0;
|
||||
} unreachable;
|
||||
}
|
||||
|
||||
auto readHalf(u32 address) -> u16 {
|
||||
auto data = ((T*)this)->readWord(address);
|
||||
switch(address & 2) {
|
||||
case 0: return data >> 16;
|
||||
case 2: return data >> 0;
|
||||
} unreachable;
|
||||
}
|
||||
|
||||
auto readDouble(u32 address) -> u64 {
|
||||
u64 data = ((T*)this)->readWord(address);
|
||||
return data << 32 | ((T*)this)->readWord(address + 4);
|
||||
}
|
||||
|
||||
auto writeByte(u32 address, u8 data) -> void {
|
||||
switch(address & 3) {
|
||||
case 0: return ((T*)this)->writeWord(address, data << 24);
|
||||
case 1: return ((T*)this)->writeWord(address, data << 16);
|
||||
case 2: return ((T*)this)->writeWord(address, data << 8);
|
||||
case 3: return ((T*)this)->writeWord(address, data << 0);
|
||||
} unreachable;
|
||||
}
|
||||
|
||||
auto writeHalf(u32 address, u16 data) -> void {
|
||||
switch(address & 2) {
|
||||
case 0: return ((T*)this)->writeWord(address, data << 16);
|
||||
case 2: return ((T*)this)->writeWord(address, data << 0);
|
||||
} unreachable;
|
||||
}
|
||||
|
||||
auto writeDouble(u32 address, u64 data) -> void {
|
||||
((T*)this)->writeWord(address + 0, data >> 32);
|
||||
((T*)this)->writeWord(address + 4, data >> 0);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct Bus {
|
||||
//bus.hpp
|
||||
auto readByte(u32 address) -> u8;
|
||||
auto readHalf(u32 address) -> u16;
|
||||
auto readWord(u32 address) -> u32;
|
||||
@@ -91,10 +198,6 @@ struct Bus {
|
||||
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;
|
||||
+3
-2
@@ -5,7 +5,7 @@ static const vector<string> registerNames = {
|
||||
"MI_INTR_MASK",
|
||||
};
|
||||
|
||||
auto MI::readIO(u32 address) -> u32 {
|
||||
auto MI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
@@ -47,8 +47,9 @@ auto MI::readIO(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto MI::writeIO(u32 address, uint32 data) -> void {
|
||||
auto MI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//MI_INIT_MODE
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
//MIPS Interface
|
||||
|
||||
struct MI {
|
||||
struct MI : Memory::IO<MI> {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
@@ -27,8 +27,8 @@ struct MI {
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
+16
-14
@@ -2,8 +2,10 @@
|
||||
//started: 2020-04-28
|
||||
|
||||
#include <ares/ares.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/recompiler/amd64/amd64.hpp>
|
||||
#include <nmmintrin.h>
|
||||
using s128 = __m128i;
|
||||
using v128 = __m128i;
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
struct Accuracy {
|
||||
@@ -11,9 +13,21 @@ namespace ares::Nintendo64 {
|
||||
static constexpr bool Reference = 0;
|
||||
|
||||
struct CPU {
|
||||
//0 = dynamic recompiler; 1 = interpreter
|
||||
static constexpr bool Interpreter = 0 | Reference;
|
||||
|
||||
//exceptions when the CPU accesses unaligned memory addresses
|
||||
static constexpr bool AddressErrors = 0 | Reference;
|
||||
};
|
||||
|
||||
struct RSP {
|
||||
//0 = dynamic recompiler; 1 = interpreter
|
||||
static constexpr bool Interpreter = 0 | Reference;
|
||||
|
||||
//VU instructions
|
||||
static constexpr bool SISD = 0 | Reference;
|
||||
static constexpr bool SIMD = SISD == 0;
|
||||
};
|
||||
};
|
||||
|
||||
struct Region {
|
||||
@@ -33,23 +47,11 @@ namespace ares::Nintendo64 {
|
||||
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/dd/dd.hpp>
|
||||
#include <n64/mi/mi.hpp>
|
||||
#include <n64/vi/vi.hpp>
|
||||
#include <n64/ai/ai.hpp>
|
||||
|
||||
+5
-4
@@ -14,7 +14,7 @@ static const vector<string> registerNames = {
|
||||
"PI_BSD_DOM2_RLS",
|
||||
};
|
||||
|
||||
auto PI::readIO(u32 address) -> u32 {
|
||||
auto PI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
@@ -92,8 +92,9 @@ auto PI::readIO(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto PI::writeIO(u32 address, uint32 data) -> void {
|
||||
auto PI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
//only PI_STATUS can be written while PI is busy
|
||||
if(address != 4 && (io.dmaBusy || io.ioBusy)) {
|
||||
@@ -115,7 +116,7 @@ auto PI::writeIO(u32 address, uint32 data) -> void {
|
||||
//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);
|
||||
u16 data = bus.readHalf(io.dramAddress + address);
|
||||
bus.writeHalf(io.pbusAddress + address, data);
|
||||
}
|
||||
io.interrupt = 1;
|
||||
@@ -127,7 +128,7 @@ auto PI::writeIO(u32 address, uint32 data) -> void {
|
||||
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);
|
||||
bus.writeHalf(io.dramAddress + address, data);
|
||||
}
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
|
||||
+1
-3
@@ -32,9 +32,7 @@ auto PI::power() -> void {
|
||||
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());
|
||||
}
|
||||
rom.load(fp);
|
||||
}
|
||||
|
||||
//write CIC seeds into PIF RAM so that cartridge checksum function passes
|
||||
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
//Peripheral Interface
|
||||
|
||||
struct PI {
|
||||
struct PI : Memory::IO<PI> {
|
||||
Node::Component node;
|
||||
Memory rom;
|
||||
Memory ram;
|
||||
Memory::Readable rom;
|
||||
Memory::Writable ram;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
@@ -25,8 +25,8 @@ struct PI {
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
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)});
|
||||
}
|
||||
}
|
||||
+152
-20
@@ -1,69 +1,201 @@
|
||||
static const vector<string> registerNames = {
|
||||
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::readWord(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::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
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;
|
||||
if(command.end > command.current) {
|
||||
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)});
|
||||
}
|
||||
}
|
||||
|
||||
static const vector<string> registerNamesIO = {
|
||||
"DPS_TBIST",
|
||||
"DPS_TEST_MODE",
|
||||
"DPS_BUFTEST_ADDR",
|
||||
"DPS_BUFTEST_DATA",
|
||||
};
|
||||
|
||||
auto RDP::readIO(u32 address) -> u32 {
|
||||
auto RDP::IO::readWord(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;
|
||||
data.bit(0) = bist.check;
|
||||
data.bit(1) = bist.go;
|
||||
data.bit(2) = bist.done;
|
||||
data.bit(3,10) = bist.fail;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPS_TEST_MODE
|
||||
data.bit(0) = io.test.enable;
|
||||
data.bit(0) = test.enable;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//DPS_BUFTEST_ADDR
|
||||
data.bit(0,6) = io.test.address;
|
||||
data.bit(0,6) = test.address;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//DPS_BUFTEST_DATA
|
||||
data.bit(0,31) = io.test.data;
|
||||
data.bit(0,31) = test.data;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "DPS_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
if(self.debugger.tracer.io->enabled()) {
|
||||
self.debugger.io({registerNamesIO(address, "DPS_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RDP::writeIO(u32 address, uint32 data) -> void {
|
||||
auto RDP::IO::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
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;
|
||||
bist.check = data.bit(0);
|
||||
bist.go = data.bit(1);
|
||||
if(data.bit(2)) bist.done = 0;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//DPS_TEST_MODE
|
||||
io.test.enable = data.bit(0);
|
||||
test.enable = data.bit(0);
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//DPS_BUFTEST_ADDR
|
||||
io.test.address = data.bit(0,6);
|
||||
test.address = data.bit(0,6);
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//DPS_BUFTEST_DATA
|
||||
io.test.data = data.bit(0,31);
|
||||
test.data = data.bit(0,31);
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "DPS_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
if(self.debugger.tracer.io->enabled()) {
|
||||
self.debugger.io({registerNamesIO(address, "DPS_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace ares::Nintendo64 {
|
||||
RDP rdp;
|
||||
#include "render.cpp"
|
||||
#include "io.cpp"
|
||||
#include "io-scc.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@@ -31,7 +30,8 @@ auto RDP::power() -> void {
|
||||
Thread::reset();
|
||||
command = {};
|
||||
set = {};
|
||||
io = {};
|
||||
io.bist = {};
|
||||
io.test = {};
|
||||
}
|
||||
|
||||
}
|
||||
+267
-21
@@ -1,6 +1,6 @@
|
||||
//Reality Display Processor
|
||||
|
||||
struct RDP : Thread {
|
||||
struct RDP : Thread, Memory::IO<RDP> {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
@@ -27,30 +27,45 @@ struct RDP : Thread {
|
||||
auto render() -> void;
|
||||
auto noOperation() -> void;
|
||||
auto invalidOperation() -> void;
|
||||
auto unshadedTriangle() -> void;
|
||||
auto unshadedZbufferTriangle() -> void;
|
||||
auto textureTriangle() -> void;
|
||||
auto textureZbufferTriangle() -> void;
|
||||
auto shadedTriangle() -> void;
|
||||
auto shadedZbufferTriangle() -> void;
|
||||
auto shadedTextureTriangle() -> void;
|
||||
auto shadedTextureZbufferTriangle() -> 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;
|
||||
auto setKeyGB() -> void;
|
||||
auto setKeyR() -> void;
|
||||
auto setConvert() -> void;
|
||||
auto setScissor() -> void;
|
||||
auto setPrimitiveDepth() -> void;
|
||||
auto setOtherModes() -> void;
|
||||
auto textureRectangle() -> void;
|
||||
auto textureRectangleFlip() -> void;
|
||||
auto loadTLUT() -> void;
|
||||
auto setTileSize() -> void;
|
||||
auto loadBlock() -> void;
|
||||
auto loadTile() -> void;
|
||||
auto setTile() -> void;
|
||||
auto fillRectangle() -> void;
|
||||
auto setFillColor() -> void;
|
||||
auto setFogColor() -> void;
|
||||
auto setBlendColor() -> void;
|
||||
auto setPrimitiveColor() -> void;
|
||||
auto setEnvironmentColor() -> void;
|
||||
auto setCombineMode() -> void;
|
||||
auto setTextureImage() -> void;
|
||||
auto setMaskImage() -> void;
|
||||
auto setColorImage() -> 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;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@@ -69,6 +84,194 @@ struct RDP : Thread {
|
||||
uint1 ready = 1;
|
||||
} command;
|
||||
|
||||
struct Point {
|
||||
uint16 i; //integer
|
||||
uint16 f; //fraction
|
||||
};
|
||||
|
||||
struct Edge {
|
||||
uint1 lmajor;
|
||||
uint3 level;
|
||||
uint3 tile;
|
||||
struct Y {
|
||||
uint14 hi;
|
||||
uint14 md;
|
||||
uint14 lo;
|
||||
} y;
|
||||
struct X {
|
||||
struct {
|
||||
Point c; //coordinate
|
||||
Point s; //inverse slope
|
||||
} hi, md, lo;
|
||||
} x;
|
||||
} edge;
|
||||
|
||||
struct Shade {
|
||||
struct Channel {
|
||||
Point c; //color
|
||||
Point x; //change per X coordinate
|
||||
Point y; //change per Y coordinate
|
||||
Point e; //change along edge
|
||||
} r, g, b, a;
|
||||
} shade;
|
||||
|
||||
struct Texture {
|
||||
struct {
|
||||
Point c; //coordinate
|
||||
Point x; //change per X coordinate
|
||||
Point y; //change per Y coordinate
|
||||
Point e; //change along edge
|
||||
} s, t, w;
|
||||
} texture;
|
||||
|
||||
struct Zbuffer {
|
||||
Point d; //inverse depth
|
||||
Point x; //change per X coordinate
|
||||
Point y; //change per Y coordinate
|
||||
Point e; //change along edge
|
||||
} zbuffer;
|
||||
|
||||
struct TextureRectangle {
|
||||
uint3 tile;
|
||||
struct {
|
||||
uint12 hi;
|
||||
uint12 lo;
|
||||
} x, y;
|
||||
Point s;
|
||||
Point t;
|
||||
} rectangle;
|
||||
|
||||
struct OtherModes {
|
||||
uint1 atomicPrimitive;
|
||||
uint1 reserved1;
|
||||
uint2 cycleType;
|
||||
uint1 perspective;
|
||||
uint1 detailTexture;
|
||||
uint1 sharpenTexture;
|
||||
uint1 lodTexture;
|
||||
uint1 tlut;
|
||||
uint1 tlutType;
|
||||
uint1 sampleType;
|
||||
uint1 midTexel;
|
||||
uint1 bilerp[2];
|
||||
uint1 convertOne;
|
||||
uint1 colorKey;
|
||||
uint2 colorDitherMode;
|
||||
uint2 alphaDitherMode;
|
||||
uint4 reserved2;
|
||||
uint2 blend1a[2];
|
||||
uint2 blend1b[2];
|
||||
uint2 blend2a[2];
|
||||
uint2 blend2b[2];
|
||||
uint1 reserved3;
|
||||
uint1 forceBlend;
|
||||
uint1 alphaCoverage;
|
||||
uint1 coverageXalpha;
|
||||
uint2 zMode;
|
||||
uint2 coverageMode;
|
||||
uint1 colorOnCoverage;
|
||||
uint1 imageRead;
|
||||
uint1 zUpdate;
|
||||
uint1 zCompare;
|
||||
uint1 antialias;
|
||||
uint1 zSource;
|
||||
uint1 ditherAlpha;
|
||||
uint1 alphaCompare;
|
||||
} other;
|
||||
|
||||
struct FogColor {
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
uint8 alpha;
|
||||
} fog;
|
||||
|
||||
struct Blend {
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
uint8 alpha;
|
||||
} blend;
|
||||
|
||||
struct PrimitiveColor {
|
||||
uint5 minimum;
|
||||
uint8 fraction;
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
uint8 alpha;
|
||||
} primitive;
|
||||
|
||||
struct EnvironmentColor {
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
uint8 alpha;
|
||||
} environment;
|
||||
|
||||
struct CombineMode {
|
||||
struct MUL {
|
||||
uint5 color[2];
|
||||
uint3 alpha[2];
|
||||
} mul;
|
||||
struct ADD {
|
||||
uint3 color[2];
|
||||
uint3 alpha[2];
|
||||
} add;
|
||||
struct SUB {
|
||||
uint4 color[2];
|
||||
uint3 alpha[2];
|
||||
} sba, sbb;
|
||||
} combine;
|
||||
|
||||
struct TLUT {
|
||||
uint3 index;
|
||||
struct {
|
||||
uint12 lo;
|
||||
uint12 hi;
|
||||
} s, t;
|
||||
} tlut;
|
||||
|
||||
struct Load {
|
||||
struct Block {
|
||||
uint3 index;
|
||||
struct {
|
||||
uint12 lo;
|
||||
uint12 hi;
|
||||
} s, t;
|
||||
} block;
|
||||
struct Tile {
|
||||
uint3 index;
|
||||
struct {
|
||||
uint12 lo;
|
||||
uint12 hi;
|
||||
} s, t;
|
||||
} tile;
|
||||
} load_;
|
||||
|
||||
struct TileSize {
|
||||
uint3 index;
|
||||
struct {
|
||||
uint12 lo;
|
||||
uint12 hi;
|
||||
} s, t;
|
||||
} tileSize;
|
||||
|
||||
struct Tile {
|
||||
uint3 format;
|
||||
uint2 size;
|
||||
uint9 line;
|
||||
uint9 address;
|
||||
uint3 index;
|
||||
uint4 palette;
|
||||
struct {
|
||||
uint1 clamp;
|
||||
uint1 mirror;
|
||||
uint4 mask;
|
||||
uint4 shift;
|
||||
} s, t;
|
||||
} tile;
|
||||
|
||||
struct Set {
|
||||
struct Fill {
|
||||
uint32 color = 0;
|
||||
@@ -79,6 +282,9 @@ struct RDP : Thread {
|
||||
uint10 width = 0;
|
||||
uint26 dramAddress = 0;
|
||||
} texture;
|
||||
struct Mask {
|
||||
uint26 dramAddress = 0;
|
||||
} mask;
|
||||
struct Color {
|
||||
uint3 format = 0;
|
||||
uint2 size = 0;
|
||||
@@ -87,7 +293,47 @@ struct RDP : Thread {
|
||||
} color;
|
||||
} set;
|
||||
|
||||
struct IO {
|
||||
struct PrimitiveDepth {
|
||||
uint16 z;
|
||||
uint16 deltaZ;
|
||||
} primitiveDepth;
|
||||
|
||||
struct Scissor {
|
||||
uint1 field;
|
||||
uint1 odd;
|
||||
struct {
|
||||
uint12 lo;
|
||||
uint12 hi;
|
||||
} x, y;
|
||||
} scissor;
|
||||
|
||||
struct Convert {
|
||||
uint9 k[6];
|
||||
} convert;
|
||||
|
||||
struct Key {
|
||||
struct {
|
||||
uint12 width;
|
||||
uint8 center;
|
||||
uint8 scale;
|
||||
} r, g, b;
|
||||
} key;
|
||||
|
||||
struct FillRectangle {
|
||||
struct {
|
||||
uint12 lo;
|
||||
uint12 hi;
|
||||
} x, y;
|
||||
} fillRectangle_;
|
||||
|
||||
struct IO : Memory::IO<IO> {
|
||||
RDP& self;
|
||||
IO(RDP& self) : self(self) {}
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
struct BIST {
|
||||
uint1 check = 0;
|
||||
uint1 go = 0;
|
||||
@@ -99,7 +345,7 @@ struct RDP : Thread {
|
||||
uint7 address = 0;
|
||||
uint32 data = 0;
|
||||
} test;
|
||||
} io;
|
||||
} io{*this};
|
||||
};
|
||||
|
||||
extern RDP rdp;
|
||||
+433
-123
@@ -46,12 +46,141 @@ static const vector<string> commandNames = {
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
auto fetchEdge = [&](u64 op) {
|
||||
edge.lmajor = uint1(op >> 55);
|
||||
edge.level = uint3(op >> 51);
|
||||
edge.tile = uint3(op >> 48);
|
||||
edge.y.lo = uint14(op >> 32);
|
||||
edge.y.md = uint14(op >> 16);
|
||||
edge.y.hi = uint14(op >> 0);
|
||||
op = fetch();
|
||||
edge.x.lo.c.i = uint16(op >> 48);
|
||||
edge.x.lo.c.f = uint16(op >> 32);
|
||||
edge.x.lo.s.i = uint16(op >> 16);
|
||||
edge.x.lo.s.f = uint16(op >> 0);
|
||||
op = fetch();
|
||||
edge.x.hi.c.i = uint16(op >> 48);
|
||||
edge.x.hi.c.f = uint16(op >> 32);
|
||||
edge.x.hi.s.i = uint16(op >> 16);
|
||||
edge.x.hi.s.f = uint16(op >> 0);
|
||||
op = fetch();
|
||||
edge.x.md.c.i = uint16(op >> 48);
|
||||
edge.x.md.c.f = uint16(op >> 32);
|
||||
edge.x.md.s.i = uint16(op >> 16);
|
||||
edge.x.md.s.f = uint16(op >> 0);
|
||||
};
|
||||
|
||||
auto fetchShade = [&](u64 op) {
|
||||
op = fetch();
|
||||
shade.r.c.i = uint16(op >> 48);
|
||||
shade.g.c.i = uint16(op >> 32);
|
||||
shade.b.c.i = uint16(op >> 16);
|
||||
shade.a.c.i = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.x.i = uint16(op >> 48);
|
||||
shade.g.x.i = uint16(op >> 32);
|
||||
shade.b.x.i = uint16(op >> 16);
|
||||
shade.a.x.i = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.c.f = uint16(op >> 48);
|
||||
shade.g.c.f = uint16(op >> 32);
|
||||
shade.b.c.f = uint16(op >> 16);
|
||||
shade.a.c.f = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.x.f = uint16(op >> 48);
|
||||
shade.g.x.f = uint16(op >> 32);
|
||||
shade.b.x.f = uint16(op >> 16);
|
||||
shade.a.x.f = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.e.i = uint16(op >> 48);
|
||||
shade.g.e.i = uint16(op >> 32);
|
||||
shade.b.e.i = uint16(op >> 16);
|
||||
shade.a.e.i = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.y.i = uint16(op >> 48);
|
||||
shade.g.y.i = uint16(op >> 32);
|
||||
shade.b.y.i = uint16(op >> 16);
|
||||
shade.a.y.i = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.e.f = uint16(op >> 48);
|
||||
shade.g.e.f = uint16(op >> 32);
|
||||
shade.b.e.f = uint16(op >> 16);
|
||||
shade.a.e.f = uint16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.y.f = uint16(op >> 48);
|
||||
shade.g.y.f = uint16(op >> 32);
|
||||
shade.b.y.f = uint16(op >> 16);
|
||||
shade.a.y.f = uint16(op >> 0);
|
||||
};
|
||||
|
||||
auto fetchTexture = [&](u64 op) {
|
||||
op = fetch();
|
||||
texture.s.c.i = uint16(op >> 48);
|
||||
texture.t.c.i = uint16(op >> 32);
|
||||
texture.w.c.i = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.x.i = uint16(op >> 48);
|
||||
texture.t.x.i = uint16(op >> 32);
|
||||
texture.w.x.i = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.c.f = uint16(op >> 48);
|
||||
texture.t.c.f = uint16(op >> 32);
|
||||
texture.w.c.f = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.x.f = uint16(op >> 48);
|
||||
texture.t.x.f = uint16(op >> 32);
|
||||
texture.w.x.f = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.e.i = uint16(op >> 48);
|
||||
texture.t.e.i = uint16(op >> 32);
|
||||
texture.w.e.i = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.y.i = uint16(op >> 48);
|
||||
texture.t.y.i = uint16(op >> 32);
|
||||
texture.w.y.i = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.e.f = uint16(op >> 48);
|
||||
texture.t.e.f = uint16(op >> 32);
|
||||
texture.w.e.f = uint16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.y.f = uint16(op >> 48);
|
||||
texture.t.y.f = uint16(op >> 32);
|
||||
texture.w.y.f = uint16(op >> 16);
|
||||
};
|
||||
|
||||
auto fetchZBuffer = [&](u64 op) {
|
||||
op = fetch();
|
||||
zbuffer.d.i = uint16(op >> 48);
|
||||
zbuffer.d.f = uint16(op >> 32);
|
||||
zbuffer.x.i = uint16(op >> 16);
|
||||
zbuffer.x.f = uint16(op >> 0);
|
||||
op = fetch();
|
||||
zbuffer.e.i = uint16(op >> 48);
|
||||
zbuffer.e.f = uint16(op >> 32);
|
||||
zbuffer.y.i = uint16(op >> 16);
|
||||
zbuffer.y.f = uint16(op >> 0);
|
||||
};
|
||||
|
||||
auto fetchRectangle = [&](u64 op) {
|
||||
rectangle.x.lo = uint12(op >> 44);
|
||||
rectangle.y.lo = uint12(op >> 32);
|
||||
rectangle.tile = uint3(op >> 24);
|
||||
rectangle.x.hi = uint12(op >> 12);
|
||||
rectangle.y.hi = uint12(op >> 0);
|
||||
op = fetch();
|
||||
rectangle.s.i = uint16(op >> 48);
|
||||
rectangle.t.i = uint16(op >> 32);
|
||||
rectangle.s.f = uint16(op >> 16);
|
||||
rectangle.t.f = uint16(op >> 0);
|
||||
};
|
||||
|
||||
while(command.current < command.end) {
|
||||
u64 op = fetch();
|
||||
|
||||
@@ -76,55 +205,55 @@ auto RDP::render() -> void {
|
||||
} break;
|
||||
|
||||
case 0x08: {
|
||||
command.current += 8 * 3; //edge
|
||||
//unshadedTriangle();
|
||||
fetchEdge(op);
|
||||
unshadedTriangle();
|
||||
} break;
|
||||
|
||||
case 0x09: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//unshadedZbufferTriangle();
|
||||
fetchEdge(op);
|
||||
fetchZBuffer(op);
|
||||
unshadedZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0a: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //texture
|
||||
//textureTriangle();
|
||||
fetchEdge(op);
|
||||
fetchTexture(op);
|
||||
textureTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0b: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //texture
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//textureZbufferTriangle();
|
||||
fetchEdge(op);
|
||||
fetchTexture(op);
|
||||
fetchZBuffer(op);
|
||||
textureZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0c: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
//shadedTriangle();
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
shadedTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0d: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
command.current += 8 * 2; //zbuffer
|
||||
//shadedZbufferTriangle();
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
fetchZBuffer(op);
|
||||
shadedZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0e: {
|
||||
command.current += 8 * 3; //edge
|
||||
command.current += 8 * 8; //shade
|
||||
command.current += 8 * 8; //texture
|
||||
//shadedTextureTriangle();
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
fetchTexture(op);
|
||||
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();
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
fetchTexture(op);
|
||||
fetchZBuffer(op);
|
||||
shadedTextureZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x10:
|
||||
@@ -151,31 +280,13 @@ auto RDP::render() -> void {
|
||||
} 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);
|
||||
fetchRectangle(op);
|
||||
textureRectangle();
|
||||
} 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);
|
||||
fetchRectangle(op);
|
||||
textureRectangleFlip();
|
||||
} break;
|
||||
|
||||
case 0x26: {
|
||||
@@ -195,31 +306,99 @@ auto RDP::render() -> void {
|
||||
} break;
|
||||
|
||||
case 0x2a: {
|
||||
//setKeyGB();
|
||||
key.g.width = uint12(op >> 44);
|
||||
key.b.width = uint12(op >> 32);
|
||||
key.g.center = uint8(op >> 24);
|
||||
key.g.scale = uint8(op >> 16);
|
||||
key.b.center = uint8(op >> 8);
|
||||
key.b.scale = uint8(op >> 0);
|
||||
setKeyGB();
|
||||
} break;
|
||||
|
||||
case 0x2b: {
|
||||
//setKeyR();
|
||||
key.r.width = uint12(op >> 16);
|
||||
key.r.center = uint8(op >> 8);
|
||||
key.r.scale = uint8(op >> 0);
|
||||
setKeyR();
|
||||
} break;
|
||||
|
||||
case 0x2c: {
|
||||
//setConvert();
|
||||
convert.k[0] = uint9(op >> 45);
|
||||
convert.k[1] = uint9(op >> 36);
|
||||
convert.k[2] = uint9(op >> 27);
|
||||
convert.k[3] = uint9(op >> 18);
|
||||
convert.k[4] = uint9(op >> 9);
|
||||
convert.k[5] = uint8(op >> 0);
|
||||
setConvert();
|
||||
} break;
|
||||
|
||||
case 0x2d: {
|
||||
//setScissor();
|
||||
scissor.x.hi = uint12(op >> 44);
|
||||
scissor.y.hi = uint12(op >> 32);
|
||||
scissor.field = uint1(op >> 25);
|
||||
scissor.odd = uint1(op >> 24);
|
||||
scissor.x.lo = uint12(op >> 12);
|
||||
scissor.y.lo = uint12(op >> 0);
|
||||
setScissor();
|
||||
} break;
|
||||
|
||||
case 0x2e: {
|
||||
//setPrimitiveDepth();
|
||||
primitiveDepth.z = uint16(op >> 16);
|
||||
primitiveDepth.deltaZ = uint16(op >> 0);
|
||||
setPrimitiveDepth();
|
||||
} break;
|
||||
|
||||
case 0x2f: {
|
||||
//setOtherModes();
|
||||
other.atomicPrimitive = uint1(op >> 55);
|
||||
other.reserved1 = uint1(op >> 54);
|
||||
other.cycleType = uint2(op >> 52);
|
||||
other.perspective = uint1(op >> 51);
|
||||
other.detailTexture = uint1(op >> 50);
|
||||
other.sharpenTexture = uint1(op >> 49);
|
||||
other.lodTexture = uint1(op >> 48);
|
||||
other.tlut = uint1(op >> 47);
|
||||
other.tlutType = uint1(op >> 46);
|
||||
other.sampleType = uint1(op >> 45);
|
||||
other.midTexel = uint1(op >> 44);
|
||||
other.bilerp[0] = uint1(op >> 43);
|
||||
other.bilerp[1] = uint1(op >> 42);
|
||||
other.convertOne = uint1(op >> 41);
|
||||
other.colorKey = uint1(op >> 40);
|
||||
other.colorDitherMode = uint2(op >> 38);
|
||||
other.alphaDitherMode = uint2(op >> 36);
|
||||
other.reserved2 = uint4(op >> 32);
|
||||
other.blend1a[0] = uint2(op >> 30);
|
||||
other.blend1a[1] = uint2(op >> 28);
|
||||
other.blend1b[0] = uint2(op >> 26);
|
||||
other.blend1b[1] = uint2(op >> 24);
|
||||
other.blend2a[0] = uint2(op >> 22);
|
||||
other.blend2a[1] = uint2(op >> 20);
|
||||
other.blend2b[0] = uint2(op >> 18);
|
||||
other.blend2b[1] = uint2(op >> 16);
|
||||
other.reserved3 = uint1(op >> 15);
|
||||
other.forceBlend = uint1(op >> 14);
|
||||
other.alphaCoverage = uint1(op >> 13);
|
||||
other.coverageXalpha = uint1(op >> 12);
|
||||
other.zMode = uint2(op >> 10);
|
||||
other.coverageMode = uint2(op >> 8);
|
||||
other.colorOnCoverage = uint1(op >> 7);
|
||||
other.imageRead = uint1(op >> 6);
|
||||
other.zUpdate = uint1(op >> 5);
|
||||
other.zCompare = uint1(op >> 4);
|
||||
other.antialias = uint1(op >> 3);
|
||||
other.zSource = uint1(op >> 2);
|
||||
other.ditherAlpha = uint1(op >> 1);
|
||||
other.alphaCompare = uint1(op >> 0);
|
||||
setOtherModes();
|
||||
} break;
|
||||
|
||||
case 0x30: {
|
||||
//loadTextureLUT();
|
||||
tlut.s.lo = uint12(op >> 44);
|
||||
tlut.t.lo = uint12(op >> 32);
|
||||
tlut.index = uint3(op >> 24);
|
||||
tlut.s.hi = uint12(op >> 12);
|
||||
tlut.t.hi = uint12(op >> 0);
|
||||
loadTLUT();
|
||||
} break;
|
||||
|
||||
case 0x31: {
|
||||
@@ -227,72 +406,136 @@ auto RDP::render() -> void {
|
||||
} break;
|
||||
|
||||
case 0x32: {
|
||||
//setTileSize();
|
||||
tileSize.s.lo = uint12(op >> 44);
|
||||
tileSize.t.lo = uint12(op >> 32);
|
||||
tileSize.index = uint3(op >> 24);
|
||||
tileSize.s.hi = uint12(op >> 12);
|
||||
tileSize.t.hi = uint12(op >> 0);
|
||||
setTileSize();
|
||||
} break;
|
||||
|
||||
case 0x33: {
|
||||
//loadBlock();
|
||||
load_.block.s.lo = uint12(op >> 44);
|
||||
load_.block.t.lo = uint12(op >> 32);
|
||||
load_.block.index = uint3(op >> 24);
|
||||
load_.block.s.hi = uint12(op >> 12);
|
||||
load_.block.t.hi = uint12(op >> 0);
|
||||
loadBlock();
|
||||
} break;
|
||||
|
||||
case 0x34: {
|
||||
//loadTile();
|
||||
load_.tile.s.lo = uint12(op >> 44);
|
||||
load_.tile.t.lo = uint12(op >> 32);
|
||||
load_.tile.index = uint3(op >> 24);
|
||||
load_.tile.s.hi = uint12(op >> 12);
|
||||
load_.tile.t.hi = uint12(op >> 0);
|
||||
loadTile();
|
||||
} break;
|
||||
|
||||
case 0x35: {
|
||||
//setTile();
|
||||
tile.format = uint3(op >> 53);
|
||||
tile.size = uint2(op >> 51);
|
||||
tile.line = uint9(op >> 41);
|
||||
tile.address = uint9(op >> 32);
|
||||
tile.index = uint3(op >> 24);
|
||||
tile.palette = uint4(op >> 20);
|
||||
tile.t.clamp = uint1(op >> 19);
|
||||
tile.t.mirror = uint1(op >> 18);
|
||||
tile.t.mask = uint4(op >> 14);
|
||||
tile.t.shift = uint4(op >> 10);
|
||||
tile.s.clamp = uint1(op >> 9);
|
||||
tile.s.mirror = uint1(op >> 8);
|
||||
tile.s.mask = uint4(op >> 4);
|
||||
tile.s.shift = uint4(op >> 0);
|
||||
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);
|
||||
fillRectangle_.x.lo = uint12(op >> 44);
|
||||
fillRectangle_.y.lo = uint12(op >> 32);
|
||||
fillRectangle_.x.hi = uint12(op >> 12);
|
||||
fillRectangle_.y.hi = uint12(op >> 0);
|
||||
fillRectangle();
|
||||
} break;
|
||||
|
||||
case 0x37: {
|
||||
auto color = uint32(op >> 0);
|
||||
setFillColor(color);
|
||||
set.fill.color = uint32(op >> 0);
|
||||
setFillColor();
|
||||
} break;
|
||||
|
||||
case 0x38: {
|
||||
//setFogColor();
|
||||
fog.red = uint8(op >> 24);
|
||||
fog.green = uint8(op >> 16);
|
||||
fog.blue = uint8(op >> 8);
|
||||
fog.alpha = uint8(op >> 0);
|
||||
setFogColor();
|
||||
} break;
|
||||
|
||||
case 0x39: {
|
||||
//setBlendColor();
|
||||
blend.red = uint8(op >> 24);
|
||||
blend.green = uint8(op >> 16);
|
||||
blend.blue = uint8(op >> 8);
|
||||
blend.alpha = uint8(op >> 0);
|
||||
setBlendColor();
|
||||
} break;
|
||||
|
||||
case 0x3a: {
|
||||
//setPrimitiveColor();
|
||||
primitive.minimum = uint4(op >> 40);
|
||||
primitive.fraction = uint8(op >> 32);
|
||||
primitive.red = uint8(op >> 24);
|
||||
primitive.green = uint8(op >> 16);
|
||||
primitive.blue = uint8(op >> 8);
|
||||
primitive.alpha = uint8(op >> 0);
|
||||
setPrimitiveColor();
|
||||
} break;
|
||||
|
||||
case 0x3b: {
|
||||
//setEnvironmentColor();
|
||||
environment.red = uint8(op >> 24);
|
||||
environment.green = uint8(op >> 16);
|
||||
environment.blue = uint8(op >> 8);
|
||||
environment.alpha = uint8(op >> 0);
|
||||
setEnvironmentColor();
|
||||
} break;
|
||||
|
||||
case 0x3c: {
|
||||
//setCombineMode();
|
||||
combine.sba.color[0] = uint4(op >> 52);
|
||||
combine.mul.color[0] = uint5(op >> 47);
|
||||
combine.sba.alpha[0] = uint3(op >> 44);
|
||||
combine.mul.alpha[0] = uint3(op >> 41);
|
||||
combine.sba.color[1] = uint4(op >> 37);
|
||||
combine.mul.color[1] = uint5(op >> 32);
|
||||
combine.sbb.color[0] = uint4(op >> 28);
|
||||
combine.sbb.color[1] = uint4(op >> 24);
|
||||
combine.sba.alpha[1] = uint3(op >> 21);
|
||||
combine.mul.alpha[1] = uint3(op >> 18);
|
||||
combine.add.color[0] = uint3(op >> 15);
|
||||
combine.sbb.alpha[0] = uint3(op >> 12);
|
||||
combine.add.alpha[0] = uint3(op >> 9);
|
||||
combine.add.color[1] = uint3(op >> 6);
|
||||
combine.sbb.alpha[1] = uint3(op >> 3);
|
||||
combine.add.alpha[1] = uint3(op >> 0);
|
||||
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);
|
||||
set.texture.format = uint3(op >> 53);
|
||||
set.texture.size = uint2(op >> 51);
|
||||
set.texture.width = uint10(op >> 32);
|
||||
set.texture.dramAddress = uint26(op >> 0);
|
||||
setTextureImage();
|
||||
} break;
|
||||
|
||||
case 0x3e: {
|
||||
//setMaskImage();
|
||||
set.mask.dramAddress = uint26(op >> 0);
|
||||
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);
|
||||
set.color.format = uint3(op >> 53);
|
||||
set.color.size = uint2(op >> 51);
|
||||
set.color.width = uint10(op >> 32);
|
||||
set.color.dramAddress = uint26(op >> 0);
|
||||
setColorImage();
|
||||
} break;
|
||||
|
||||
}
|
||||
@@ -307,18 +550,44 @@ auto RDP::noOperation() -> void {
|
||||
auto RDP::invalidOperation() -> void {
|
||||
}
|
||||
|
||||
//0x08
|
||||
auto RDP::unshadedTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x09
|
||||
auto RDP::unshadedZbufferTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x0a
|
||||
auto RDP::textureTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x0b
|
||||
auto RDP::textureZbufferTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x0c
|
||||
auto RDP::shadedTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x0d
|
||||
auto RDP::shadedZbufferTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x0e
|
||||
auto RDP::shadedTextureTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x0f
|
||||
auto RDP::shadedTextureZbufferTriangle() -> void {
|
||||
}
|
||||
|
||||
//0x24
|
||||
auto RDP::textureRectangle(
|
||||
uint12 xl, uint12 yl, uint3 tile, uint12 xh, uint12 yh,
|
||||
uint16 s, uint16 t, uint16 dsdx, uint16 dsdy
|
||||
) -> void {
|
||||
auto RDP::textureRectangle() -> void {
|
||||
}
|
||||
|
||||
//0x25
|
||||
auto RDP::textureRectangleFlip(
|
||||
uint12 xl, uint12 yl, uint3 tile, uint12 xh, uint12 yh,
|
||||
uint16 s, uint16 t, uint16 dsdx, uint16 dsdy
|
||||
) -> void {
|
||||
auto RDP::textureRectangleFlip() -> void {
|
||||
}
|
||||
|
||||
//0x26
|
||||
@@ -338,45 +607,86 @@ auto RDP::syncFull() -> void {
|
||||
mi.raise(MI::IRQ::DP);
|
||||
}
|
||||
|
||||
//0x2a
|
||||
auto RDP::setKeyGB() -> void {
|
||||
}
|
||||
|
||||
//0x2b
|
||||
auto RDP::setKeyR() -> void {
|
||||
}
|
||||
|
||||
//0x2c
|
||||
auto RDP::setConvert() -> void {
|
||||
}
|
||||
|
||||
//0x2d
|
||||
auto RDP::setScissor() -> void {
|
||||
}
|
||||
|
||||
//0x2e
|
||||
auto RDP::setPrimitiveDepth() -> void {
|
||||
}
|
||||
|
||||
//0x2f
|
||||
auto RDP::setOtherModes() -> void {
|
||||
}
|
||||
|
||||
//0x30
|
||||
auto RDP::loadTLUT() -> void {
|
||||
}
|
||||
|
||||
//0x32
|
||||
auto RDP::setTileSize() -> void {
|
||||
}
|
||||
|
||||
//0x33
|
||||
auto RDP::loadBlock() -> void {
|
||||
}
|
||||
|
||||
//0x34
|
||||
auto RDP::loadTile() -> void {
|
||||
}
|
||||
|
||||
//0x35
|
||||
auto RDP::setTile() -> void {
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
auto RDP::fillRectangle() -> void {
|
||||
}
|
||||
|
||||
//0x37
|
||||
auto RDP::setFillColor(uint32 color) -> void {
|
||||
set.fill.color = color;
|
||||
auto RDP::setFillColor() -> void {
|
||||
}
|
||||
|
||||
//0x38
|
||||
auto RDP::setFogColor() -> void {
|
||||
}
|
||||
|
||||
//0x39
|
||||
auto RDP::setBlendColor() -> void {
|
||||
}
|
||||
|
||||
//0x3a
|
||||
auto RDP::setPrimitiveColor() -> void {
|
||||
}
|
||||
|
||||
//0x3b
|
||||
auto RDP::setEnvironmentColor() -> void {
|
||||
}
|
||||
|
||||
//0x3c
|
||||
auto RDP::setCombineMode() -> void {
|
||||
}
|
||||
|
||||
//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;
|
||||
auto RDP::setTextureImage() -> void {
|
||||
}
|
||||
|
||||
//0x3e
|
||||
auto RDP::setMaskImage() -> void {
|
||||
}
|
||||
|
||||
//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;
|
||||
auto RDP::setColorImage() -> void {
|
||||
}
|
||||
@@ -11,7 +11,7 @@ static const vector<string> registerNames = {
|
||||
"RDRAM_DEVICE_MANUF",
|
||||
};
|
||||
|
||||
auto RDRAM::readIO(u32 address) -> u32 {
|
||||
auto RDRAM::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
u32 data = 0;
|
||||
|
||||
@@ -61,7 +61,7 @@ auto RDRAM::readIO(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RDRAM::writeIO(u32 address, u32 data) -> void {
|
||||
auto RDRAM::writeWord(u32 address, u32 data) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
|
||||
@@ -18,8 +18,9 @@ auto RDRAM::load(Node::Object parent) -> void {
|
||||
}
|
||||
|
||||
auto RDRAM::unload() -> void {
|
||||
ram.reset();
|
||||
debugger = {};
|
||||
ram.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
auto RDRAM::power() -> void {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//RAMBUS RAM
|
||||
|
||||
struct RDRAM {
|
||||
struct RDRAM : Memory::IO<RDRAM> {
|
||||
Node::Component node;
|
||||
Memory ram;
|
||||
Memory::Writable ram;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
@@ -24,8 +24,8 @@ struct RDRAM {
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
+3
-2
@@ -9,7 +9,7 @@ static const vector<string> registerNames = {
|
||||
"RI_WERROR",
|
||||
};
|
||||
|
||||
auto RI::readIO(u32 address) -> u32 {
|
||||
auto RI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = 0;
|
||||
|
||||
@@ -51,8 +51,9 @@ auto RI::readIO(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RI::writeIO(u32 address, u32 data) -> void {
|
||||
auto RI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//RI_MODE
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
//RDRAM Interface
|
||||
|
||||
struct RI {
|
||||
struct RI : Memory::IO<RI> {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
@@ -19,8 +19,8 @@ struct RI {
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
@@ -1,11 +1,57 @@
|
||||
#include "registers.hpp"
|
||||
#include "instruction.cpp"
|
||||
#include "vu-registers.cpp"
|
||||
#include "decoder.cpp"
|
||||
#include "cpu-instructions.cpp"
|
||||
#include "scc-instructions.cpp"
|
||||
#include "vu-instructions.cpp"
|
||||
#include "recompiler.cpp"
|
||||
#include "disassembler.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto RSP::instruction() -> void {
|
||||
if constexpr(Accuracy::RSP::Interpreter == 0) {
|
||||
auto block = recompiler.block(PC);
|
||||
block->execute();
|
||||
step(block->step);
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::Interpreter == 1) {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = imem.readWord(pipeline.address);
|
||||
debugger.instruction();
|
||||
//instructionDebug();
|
||||
decoderEXECUTE();
|
||||
instructionEpilogue();
|
||||
step(3);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionEpilogue() -> bool {
|
||||
GPR[0].u32 = 0;
|
||||
|
||||
switch(branch.state) {
|
||||
case Branch::Step: PC += 4; return 0;
|
||||
case Branch::Take: PC += 4; branch.delaySlot(); return 0;
|
||||
case Branch::DelaySlot: PC = branch.pc; branch.reset(); return 1;
|
||||
case Branch::Halt: PC += 4; return 1;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto RSP::instructionDebug() -> void {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = imem.readWord(pipeline.address);
|
||||
|
||||
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::powerCore() -> void {
|
||||
for(uint n : range(32)) GPR[n].u32 = 0;
|
||||
PC = 0;
|
||||
@@ -37,4 +83,9 @@ auto RSP::powerCore() -> void {
|
||||
while(a * (b + 1) * (b + 1) < (u64(1) << 44)) b++;
|
||||
inverseSquareRoots[index] = u16(b >> 1);
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::Interpreter == 0) {
|
||||
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill);
|
||||
recompiler.reset();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
//{
|
||||
//core.cpp
|
||||
auto instruction() -> void;
|
||||
auto instructionEpilogue() -> bool;
|
||||
auto instructionDebug() -> void;
|
||||
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;
|
||||
//decoder.cpp
|
||||
auto decoderEXECUTE() -> void;
|
||||
auto decoderSPECIAL() -> void;
|
||||
auto decoderREGIMM() -> void;
|
||||
auto decoderCOP0() -> void;
|
||||
auto decoderCOP2() -> void;
|
||||
auto decoderLWC2() -> void;
|
||||
auto decoderSWC2() -> void;
|
||||
|
||||
auto instructionINVALID() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serializeCore(serializer&) -> void;
|
||||
@@ -18,5 +23,6 @@
|
||||
#include "cpu.hpp"
|
||||
#include "scc.hpp"
|
||||
#include "vu.hpp"
|
||||
#include "recompiler.hpp"
|
||||
#include "disassembler.hpp"
|
||||
//};
|
||||
@@ -1,152 +1,178 @@
|
||||
auto RSP::instructionADDIU() -> void {
|
||||
RT.u32 = i32(RS.u32 + IMMi16);
|
||||
auto RSP::instructionADDIU(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = i32(rs.u32 + imm);
|
||||
}
|
||||
|
||||
auto RSP::instructionADDU() -> void {
|
||||
RD.u32 = i32(RS.u32 + RT.u32);
|
||||
auto RSP::instructionADDU(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = i32(rs.u32 + rt.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionAND() -> void {
|
||||
RD.u32 = RS.u32 & RT.u32;
|
||||
auto RSP::instructionAND(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = rs.u32 & rt.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionANDI() -> void {
|
||||
RT.u32 = RS.u32 & IMMu16;
|
||||
auto RSP::instructionANDI(r32& rt, cr32& rs, u16 imm) -> void {
|
||||
rt.u32 = rs.u32 & imm;
|
||||
}
|
||||
|
||||
auto RSP::instructionB(bool take) -> void {
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
auto RSP::instructionBEQ(cr32& rs, cr32& rt, i16 imm) -> void {
|
||||
if(rs.u32 == rt.u32) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBAL(bool take) -> void {
|
||||
auto RSP::instructionBGEZ(cr32& rs, i16 imm) -> void {
|
||||
if(rs.i32 >= 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBGEZAL(cr32& rs, i16 imm) -> void {
|
||||
RA.u32 = i32(PC + 8);
|
||||
if(take) branch.take(PC + 4 + (IMMi16 << 2));
|
||||
if(rs.i32 >= 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBGTZ(cr32& rs, i16 imm) -> void {
|
||||
if(rs.i32 > 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBLEZ(cr32& rs, i16 imm) -> void {
|
||||
if(rs.i32 <= 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBLTZ(cr32& rs, i16 imm) -> void {
|
||||
if(rs.i32 < 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBLTZAL(cr32& rs, i16 imm) -> void {
|
||||
RA.u32 = i32(PC + 8);
|
||||
if(rs.i32 < 0) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBNE(cr32& rs, cr32& rt, i16 imm) -> void {
|
||||
if(rs.u32 != rt.u32) branch.take(PC + 4 + (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionBREAK() -> void {
|
||||
status.halted = 1;
|
||||
status.broken = 1;
|
||||
if(status.interruptOnBreak) mi.raise(MI::IRQ::SP);
|
||||
branch.halt();
|
||||
}
|
||||
|
||||
auto RSP::instructionCACHE() -> void {
|
||||
auto RSP::instructionCACHE(u8 cache, u8 operation) -> void {
|
||||
//todo
|
||||
}
|
||||
|
||||
auto RSP::instructionJ() -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (IMMu26 << 2));
|
||||
auto RSP::instructionJ(u32 imm) -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionJAL() -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (IMMu26 << 2));
|
||||
auto RSP::instructionJAL(u32 imm) -> void {
|
||||
RA.u32 = i32(PC + 8);
|
||||
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
|
||||
}
|
||||
|
||||
auto RSP::instructionJALR() -> void {
|
||||
branch.take(RS.u32);
|
||||
RD.u32 = i32(PC + 8);
|
||||
auto RSP::instructionJALR(r32& rd, cr32& rs) -> void {
|
||||
rd.u32 = i32(PC + 8);
|
||||
branch.take(rs.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionJR() -> void {
|
||||
branch.take(RS.u32);
|
||||
auto RSP::instructionJR(cr32& rs) -> void {
|
||||
branch.take(rs.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionLB() -> void {
|
||||
RT.u32 = i8(dmem.readByte(RS.u32 + IMMi16));
|
||||
auto RSP::instructionLB(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = i8(dmem.readByte(rs.u32 + imm));
|
||||
}
|
||||
|
||||
auto RSP::instructionLBU() -> void {
|
||||
RT.u32 = u8(dmem.readByte(RS.u32 + IMMi16));
|
||||
auto RSP::instructionLBU(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = u8(dmem.readByte(rs.u32 + imm));
|
||||
}
|
||||
|
||||
auto RSP::instructionLH() -> void {
|
||||
RT.u32 = i16(dmem.readHalfUnaligned(RS.u32 + IMMi16));
|
||||
auto RSP::instructionLH(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = i16(dmem.readHalfUnaligned(rs.u32 + imm));
|
||||
}
|
||||
|
||||
auto RSP::instructionLHU() -> void {
|
||||
RT.u32 = u16(dmem.readHalfUnaligned(RS.u32 + IMMi16));
|
||||
auto RSP::instructionLHU(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = u16(dmem.readHalfUnaligned(rs.u32 + imm));
|
||||
}
|
||||
|
||||
auto RSP::instructionLUI() -> void {
|
||||
RT.u32 = i32(IMMu16 << 16);
|
||||
auto RSP::instructionLUI(r32& rt, u16 imm) -> void {
|
||||
rt.u32 = i32(imm << 16);
|
||||
}
|
||||
|
||||
auto RSP::instructionLW() -> void {
|
||||
RT.u32 = i32(dmem.readWordUnaligned(RS.u32 + IMMi16));
|
||||
auto RSP::instructionLW(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = i32(dmem.readWordUnaligned(rs.u32 + imm));
|
||||
}
|
||||
|
||||
auto RSP::instructionNOR() -> void {
|
||||
RD.u32 = ~(RS.u32 | RT.u32);
|
||||
auto RSP::instructionNOR(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = ~(rs.u32 | rt.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionOR() -> void {
|
||||
RD.u32 = RS.u32 | RT.u32;
|
||||
auto RSP::instructionOR(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = rs.u32 | rt.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionORI() -> void {
|
||||
RT.u32 = RS.u32 | IMMu16;
|
||||
auto RSP::instructionORI(r32& rt, cr32& rs, u16 imm) -> void {
|
||||
rt.u32 = rs.u32 | imm;
|
||||
}
|
||||
|
||||
auto RSP::instructionSB() -> void {
|
||||
dmem.writeByte(RS.u32 + IMMi16, RT.u32);
|
||||
auto RSP::instructionSB(cr32& rt, cr32& rs, i16 imm) -> void {
|
||||
dmem.writeByte(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionSH() -> void {
|
||||
dmem.writeHalfUnaligned(RS.u32 + IMMi16, RT.u32);
|
||||
auto RSP::instructionSH(cr32& rt, cr32& rs, i16 imm) -> void {
|
||||
dmem.writeHalfUnaligned(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionSLL() -> void {
|
||||
RD.u32 = i32(RT.u32 << SA);
|
||||
auto RSP::instructionSLL(r32& rd, cr32& rt, u8 sa) -> void {
|
||||
rd.u32 = i32(rt.u32 << sa);
|
||||
}
|
||||
|
||||
auto RSP::instructionSLLV() -> void {
|
||||
RD.u32 = i32(RT.u32 << (RS.u32 & 31));
|
||||
auto RSP::instructionSLLV(r32& rd, cr32& rt, cr32& rs) -> void {
|
||||
rd.u32 = i32(rt.u32 << (rs.u32 & 31));
|
||||
}
|
||||
|
||||
auto RSP::instructionSLT() -> void {
|
||||
RD.u32 = RS.i32 < RT.i32;
|
||||
auto RSP::instructionSLT(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = rs.i32 < rt.i32;
|
||||
}
|
||||
|
||||
auto RSP::instructionSLTI() -> void {
|
||||
RT.u32 = RS.i32 < i32(IMMi16);
|
||||
auto RSP::instructionSLTI(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = rs.i32 < imm;
|
||||
}
|
||||
|
||||
auto RSP::instructionSLTIU() -> void {
|
||||
RT.u32 = RS.u32 < u32(IMMu16);
|
||||
auto RSP::instructionSLTIU(r32& rt, cr32& rs, i16 imm) -> void {
|
||||
rt.u32 = rs.u32 < imm;
|
||||
}
|
||||
|
||||
auto RSP::instructionSLTU() -> void {
|
||||
RD.u32 = RS.u32 < RT.u32;
|
||||
auto RSP::instructionSLTU(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = rs.u32 < rt.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionSRA() -> void {
|
||||
RD.u32 = RT.i32 >> SA;
|
||||
auto RSP::instructionSRA(r32& rd, cr32& rt, u8 sa) -> void {
|
||||
rd.u32 = rt.i32 >> sa;
|
||||
}
|
||||
|
||||
auto RSP::instructionSRAV() -> void {
|
||||
RD.u32 = RT.i32 >> (RS.u32 & 31);
|
||||
auto RSP::instructionSRAV(r32& rd, cr32& rt, cr32& rs) -> void {
|
||||
rd.u32 = rt.i32 >> (rs.u32 & 31);
|
||||
}
|
||||
|
||||
auto RSP::instructionSRL() -> void {
|
||||
RD.u32 = i32(RT.u32 >> SA);
|
||||
auto RSP::instructionSRL(r32& rd, cr32& rt, u8 sa) -> void {
|
||||
rd.u32 = i32(rt.u32 >> sa);
|
||||
}
|
||||
|
||||
auto RSP::instructionSRLV() -> void {
|
||||
RD.u32 = i32(RT.u32 >> (RS.u32 & 31));
|
||||
auto RSP::instructionSRLV(r32& rd, cr32& rt, cr32& rs) -> void {
|
||||
rd.u32 = i32(rt.u32 >> (rs.u32 & 31));
|
||||
}
|
||||
|
||||
auto RSP::instructionSUBU() -> void {
|
||||
RD.u32 = i32(RS.u32 - RT.u32);
|
||||
auto RSP::instructionSUBU(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = i32(rs.u32 - rt.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionSW() -> void {
|
||||
dmem.writeWordUnaligned(RS.u32 + IMMi16, RT.u32);
|
||||
auto RSP::instructionSW(cr32& rt, cr32& rs, i16 imm) -> void {
|
||||
dmem.writeWordUnaligned(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto RSP::instructionXOR() -> void {
|
||||
RD.u32 = RS.u32 ^ RT.u32;
|
||||
auto RSP::instructionXOR(r32& rd, cr32& rs, cr32& rt) -> void {
|
||||
rd.u32 = rs.u32 ^ rt.u32;
|
||||
}
|
||||
|
||||
auto RSP::instructionXORI() -> void {
|
||||
RT.u32 = RS.u32 ^ IMMu16;
|
||||
auto RSP::instructionXORI(r32& rt, cr32& rs, u16 imm) -> void {
|
||||
rt.u32 = rs.u32 ^ imm;
|
||||
}
|
||||
+45
-37
@@ -3,6 +3,7 @@
|
||||
struct { int32_t i32; };
|
||||
struct { uint32_t u32; };
|
||||
};
|
||||
using cr32 = const r32;
|
||||
|
||||
struct Core {
|
||||
enum Register : uint {
|
||||
@@ -17,53 +18,60 @@
|
||||
} core;
|
||||
|
||||
struct Branch {
|
||||
enum : u32 { Step, Take, DelaySlot };
|
||||
enum : u32 { Step, Take, DelaySlot, Halt };
|
||||
|
||||
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 halt() -> void { state = Halt; }
|
||||
|
||||
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 instructionADDIU(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionADDU(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionAND(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionANDI(r32& rt, cr32& rs, u16 imm) -> void;
|
||||
auto instructionBEQ(cr32& rs, cr32& rt, i16 imm) -> void;
|
||||
auto instructionBGEZ(cr32& rs, i16 imm) -> void;
|
||||
auto instructionBGEZAL(cr32& rs, i16 imm) -> void;
|
||||
auto instructionBGTZ(cr32& rs, i16 imm) -> void;
|
||||
auto instructionBLEZ(cr32& rs, i16 imm) -> void;
|
||||
auto instructionBLTZ(cr32& rs, i16 imm) -> void;
|
||||
auto instructionBLTZAL(cr32& rs, i16 imm) -> void;
|
||||
auto instructionBNE(cr32& rs, cr32& rt, i16 imm) -> 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;
|
||||
auto instructionCACHE(u8 cache, u8 operation) -> void;
|
||||
auto instructionJ(u32 imm) -> void;
|
||||
auto instructionJAL(u32 imm) -> void;
|
||||
auto instructionJALR(r32& rd, cr32& rs) -> void;
|
||||
auto instructionJR(cr32& rs) -> void;
|
||||
auto instructionLB(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionLBU(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionLH(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionLHU(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionLUI(r32& rt, u16 imm) -> void;
|
||||
auto instructionLW(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionNOR(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionOR(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionORI(r32& rt, cr32& rs, u16 imm) -> void;
|
||||
auto instructionSB(cr32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionSH(cr32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionSLL(r32& rd, cr32& rt, u8 sa) -> void;
|
||||
auto instructionSLLV(r32& rd, cr32& rt, cr32& rs) -> void;
|
||||
auto instructionSLT(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionSLTI(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionSLTIU(r32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionSLTU(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionSRA(r32& rd, cr32& rt, u8 sa) -> void;
|
||||
auto instructionSRAV(r32& rd, cr32& rt, cr32& rs) -> void;
|
||||
auto instructionSRL(r32& rd, cr32& rt, u8 sa) -> void;
|
||||
auto instructionSRLV(r32& rd, cr32& rt, cr32& rs) -> void;
|
||||
auto instructionSUBU(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionSW(cr32& rt, cr32& rs, i16 imm) -> void;
|
||||
auto instructionXOR(r32& rd, cr32& rs, cr32& rt) -> void;
|
||||
auto instructionXORI(r32& rt, cr32& rs, u16 imm) -> void;
|
||||
//};
|
||||
@@ -0,0 +1,61 @@
|
||||
#define OP pipeline.instruction
|
||||
#define RD GPR[RDn]
|
||||
#define RT GPR[RTn]
|
||||
#define RS GPR[RSn]
|
||||
#define VD VPR[VDn]
|
||||
#define VS VPR[VSn]
|
||||
#define VT VPR[VTn]
|
||||
|
||||
#define jp(id, name, ...) case id: return decoder##name(__VA_ARGS__)
|
||||
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__)
|
||||
#define br(id, name, ...) case id: return instruction##name(__VA_ARGS__)
|
||||
|
||||
auto RSP::decoderEXECUTE() -> void {
|
||||
#define DECODER_EXECUTE
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::decoderSPECIAL() -> void {
|
||||
#define DECODER_SPECIAL
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::decoderREGIMM() -> void {
|
||||
#define DECODER_REGIMM
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::decoderCOP0() -> void {
|
||||
#define DECODER_COP0
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::decoderCOP2() -> void {
|
||||
#define DECODER_COP2
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::decoderLWC2() -> void {
|
||||
#define DECODER_LWC2
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::decoderSWC2() -> void {
|
||||
#define DECODER_SWC2
|
||||
#include "decoder.hpp"
|
||||
}
|
||||
|
||||
auto RSP::instructionINVALID() -> void {
|
||||
}
|
||||
|
||||
#undef jp
|
||||
#undef op
|
||||
#undef br
|
||||
|
||||
#undef OP
|
||||
#undef RD
|
||||
#undef RT
|
||||
#undef RS
|
||||
#undef VD
|
||||
#undef VS
|
||||
#undef VT
|
||||
@@ -0,0 +1,376 @@
|
||||
//shares instruction decoding between interpreter and dynamic recompiler
|
||||
|
||||
#define SA (OP >> 6 & 31)
|
||||
#define RDn (OP >> 11 & 31)
|
||||
#define RTn (OP >> 16 & 31)
|
||||
#define RSn (OP >> 21 & 31)
|
||||
#define VDn (OP >> 6 & 31)
|
||||
#define VSn (OP >> 11 & 31)
|
||||
#define VTn (OP >> 16 & 31)
|
||||
#define IMMi16 i16(OP)
|
||||
#define IMMu16 u16(OP)
|
||||
#define IMMu26 (OP & 0x03ff'ffff)
|
||||
|
||||
#ifdef DECODER_EXECUTE
|
||||
{
|
||||
switch(OP >> 26) {
|
||||
jp(0x00, SPECIAL);
|
||||
jp(0x01, REGIMM);
|
||||
br(0x02, J, IMMu26);
|
||||
br(0x03, JAL, IMMu26);
|
||||
br(0x04, BEQ, RS, RT, IMMi16);
|
||||
br(0x05, BNE, RS, RT, IMMi16);
|
||||
br(0x06, BLEZ, RS, IMMi16);
|
||||
br(0x07, BGTZ, RS, IMMi16);
|
||||
op(0x08, ADDIU, RT, RS, IMMi16); //ADDI
|
||||
op(0x09, ADDIU, RT, RS, IMMi16);
|
||||
op(0x0a, SLTI, RT, RS, IMMi16);
|
||||
op(0x0b, SLTIU, RT, RS, IMMi16);
|
||||
op(0x0c, ANDI, RT, RS, IMMu16);
|
||||
op(0x0d, ORI, RT, RS, IMMu16);
|
||||
op(0x0e, XORI, RT, RS, IMMu16);
|
||||
op(0x0f, LUI, RT, IMMu16);
|
||||
jp(0x10, COP0);
|
||||
op(0x11, INVALID); //COP1
|
||||
jp(0x12, COP2);
|
||||
op(0x13, INVALID); //COP3
|
||||
op(0x14, INVALID); //BEQL
|
||||
op(0x15, INVALID); //BNEL
|
||||
op(0x16, INVALID); //BLEZL
|
||||
op(0x17, INVALID); //BGTZL
|
||||
op(0x18, INVALID); //DADDI
|
||||
op(0x19, INVALID); //DADDIU
|
||||
op(0x1a, INVALID); //LDL
|
||||
op(0x1b, INVALID); //LDR
|
||||
op(0x1c, INVALID);
|
||||
op(0x1d, INVALID);
|
||||
op(0x1e, INVALID);
|
||||
op(0x1f, INVALID);
|
||||
op(0x20, LB, RT, RS, IMMi16);
|
||||
op(0x21, LH, RT, RS, IMMi16);
|
||||
op(0x22, INVALID); //LWL
|
||||
op(0x23, LW, RT, RS, IMMi16);
|
||||
op(0x24, LBU, RT, RS, IMMi16);
|
||||
op(0x25, LHU, RT, RS, IMMi16);
|
||||
op(0x26, INVALID); //LWR
|
||||
op(0x27, INVALID); //LWU
|
||||
op(0x28, SB, RT, RS, IMMi16);
|
||||
op(0x29, SH, RT, RS, IMMi16);
|
||||
op(0x2a, INVALID); //SWL
|
||||
op(0x2b, SW, RT, RS, IMMi16);
|
||||
op(0x2c, INVALID); //SDL
|
||||
op(0x2d, INVALID); //SDR
|
||||
op(0x2e, INVALID); //SWR
|
||||
op(0x2f, CACHE, OP >> 16 & 3, OP >> 18 & 3);
|
||||
op(0x30, INVALID); //LL
|
||||
op(0x31, INVALID); //LWC1
|
||||
jp(0x32, LWC2);
|
||||
op(0x33, INVALID); //LWC3
|
||||
op(0x34, INVALID); //LLD
|
||||
op(0x35, INVALID); //LDC1
|
||||
op(0x36, INVALID); //LDC2
|
||||
op(0x37, INVALID); //LD
|
||||
op(0x38, INVALID); //SC
|
||||
op(0x39, INVALID); //SWC1
|
||||
jp(0x3a, SWC2);
|
||||
op(0x3b, INVALID); //SWC3
|
||||
op(0x3c, INVALID); //SCD
|
||||
op(0x3d, INVALID); //SDC1
|
||||
op(0x3e, INVALID); //SDC2
|
||||
op(0x3f, INVALID); //SD
|
||||
}
|
||||
}
|
||||
#undef DECODER_EXECUTE
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_SPECIAL
|
||||
{
|
||||
switch(OP & 0x3f) {
|
||||
op(0x00, SLL, RD, RT, SA);
|
||||
op(0x01, INVALID);
|
||||
op(0x02, SRL, RD, RT, SA);
|
||||
op(0x03, SRA, RD, RT, SA);
|
||||
op(0x04, SLLV, RD, RT, RS);
|
||||
op(0x05, INVALID);
|
||||
op(0x06, SRLV, RD, RT, RS);
|
||||
op(0x07, SRAV, RD, RT, RS);
|
||||
br(0x08, JR, RS);
|
||||
br(0x09, JALR, RD, RS);
|
||||
op(0x0a, INVALID);
|
||||
op(0x0b, INVALID);
|
||||
op(0x0c, INVALID); //SYSCALL
|
||||
br(0x0d, BREAK);
|
||||
op(0x0e, INVALID);
|
||||
op(0x0f, INVALID); //SYNC
|
||||
op(0x10, INVALID); //MFHI
|
||||
op(0x11, INVALID); //MTHI
|
||||
op(0x12, INVALID); //MFLO
|
||||
op(0x13, INVALID); //MTLO
|
||||
op(0x14, INVALID); //DSLLV
|
||||
op(0x15, INVALID);
|
||||
op(0x16, INVALID); //DSRLV
|
||||
op(0x17, INVALID); //DSRAV
|
||||
op(0x18, INVALID); //MULT
|
||||
op(0x19, INVALID); //MULTU
|
||||
op(0x1a, INVALID); //DIV
|
||||
op(0x1b, INVALID); //DIVU
|
||||
op(0x1c, INVALID); //DMULT
|
||||
op(0x1d, INVALID); //DMULTU
|
||||
op(0x1e, INVALID); //DDIV
|
||||
op(0x1f, INVALID); //DDIVU
|
||||
op(0x20, ADDU, RD, RS, RT); //ADD
|
||||
op(0x21, ADDU, RD, RS, RT);
|
||||
op(0x22, SUBU, RD, RS, RT); //SUB
|
||||
op(0x23, SUBU, RD, RS, RT);
|
||||
op(0x24, AND, RD, RS, RT);
|
||||
op(0x25, OR, RD, RS, RT);
|
||||
op(0x26, XOR, RD, RS, RT);
|
||||
op(0x27, NOR, RD, RS, RT);
|
||||
op(0x28, INVALID);
|
||||
op(0x29, INVALID);
|
||||
op(0x2a, SLT, RD, RS, RT);
|
||||
op(0x2b, SLTU, RD, RS, RT);
|
||||
op(0x2c, INVALID); //DADD
|
||||
op(0x2d, INVALID); //DADDU
|
||||
op(0x2e, INVALID); //DSUB
|
||||
op(0x2f, INVALID); //DSUBU
|
||||
op(0x30, INVALID); //TGE
|
||||
op(0x31, INVALID); //TGEU
|
||||
op(0x32, INVALID); //TLT
|
||||
op(0x33, INVALID); //TLTU
|
||||
op(0x34, INVALID); //TEQ
|
||||
op(0x35, INVALID);
|
||||
op(0x36, INVALID); //TNE
|
||||
op(0x37, INVALID);
|
||||
op(0x38, INVALID); //DSLL
|
||||
op(0x39, INVALID);
|
||||
op(0x3a, INVALID); //DSRL
|
||||
op(0x3b, INVALID); //DSRA
|
||||
op(0x3c, INVALID); //DSLL32
|
||||
op(0x3d, INVALID);
|
||||
op(0x3e, INVALID); //DSRL32
|
||||
op(0x3f, INVALID); //DSRA32
|
||||
}
|
||||
}
|
||||
#undef DECODER_SPECIAL
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_REGIMM
|
||||
{
|
||||
switch(OP >> 16 & 0x1f) {
|
||||
br(0x00, BLTZ, RS, IMMi16);
|
||||
br(0x01, BGEZ, RS, IMMi16);
|
||||
op(0x02, INVALID); //BLTZL
|
||||
op(0x03, INVALID); //BGEZL
|
||||
op(0x04, INVALID);
|
||||
op(0x05, INVALID);
|
||||
op(0x06, INVALID);
|
||||
op(0x07, INVALID);
|
||||
op(0x08, INVALID); //TGEI
|
||||
op(0x09, INVALID); //TGEIU
|
||||
op(0x0a, INVALID); //TLTI
|
||||
op(0x0b, INVALID); //TLTIU
|
||||
op(0x0c, INVALID); //TEQI
|
||||
op(0x0d, INVALID);
|
||||
op(0x0e, INVALID); //TNEI
|
||||
op(0x0f, INVALID);
|
||||
br(0x10, BLTZAL, RS, IMMi16);
|
||||
br(0x11, BGEZAL, RS, IMMi16);
|
||||
op(0x12, INVALID); //BLTZALL
|
||||
op(0x13, INVALID); //BGEZALL
|
||||
op(0x14, INVALID);
|
||||
op(0x15, INVALID);
|
||||
op(0x16, INVALID);
|
||||
op(0x17, INVALID);
|
||||
op(0x18, INVALID);
|
||||
op(0x19, INVALID);
|
||||
op(0x1a, INVALID);
|
||||
op(0x1b, INVALID);
|
||||
op(0x1c, INVALID);
|
||||
op(0x1d, INVALID);
|
||||
op(0x1e, INVALID);
|
||||
op(0x1f, INVALID);
|
||||
}
|
||||
}
|
||||
#undef DECODER_REGIMM
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_COP0
|
||||
{
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
op(0x00, MFC0, RT, RDn);
|
||||
op(0x01, INVALID); //DMFC0
|
||||
op(0x02, INVALID); //CFC0
|
||||
op(0x03, INVALID);
|
||||
op(0x04, MTC0, RT, RDn);
|
||||
op(0x05, INVALID); //DMTC0
|
||||
op(0x06, INVALID); //CTC0
|
||||
op(0x07, INVALID);
|
||||
op(0x08, INVALID); //BC0
|
||||
op(0x09, INVALID);
|
||||
op(0x0a, INVALID);
|
||||
op(0x0b, INVALID);
|
||||
op(0x0c, INVALID);
|
||||
op(0x0d, INVALID);
|
||||
op(0x0e, INVALID);
|
||||
op(0x0f, INVALID);
|
||||
}
|
||||
}
|
||||
#undef DECODER_COP0
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_COP2
|
||||
{
|
||||
#define E (OP >> 7 & 15)
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
op(0x00, MFC2, RT, VS, E);
|
||||
op(0x01, INVALID); //DMFC2
|
||||
op(0x02, CFC2, RT, RDn);
|
||||
op(0x03, INVALID);
|
||||
op(0x04, MTC2, RT, VS, E);
|
||||
op(0x05, INVALID); //DMTC2
|
||||
op(0x06, CTC2, RT, RDn);
|
||||
op(0x07, INVALID);
|
||||
op(0x08, INVALID); //BC2
|
||||
op(0x09, INVALID);
|
||||
op(0x0a, INVALID);
|
||||
op(0x0b, INVALID);
|
||||
op(0x0c, INVALID);
|
||||
op(0x0d, INVALID);
|
||||
op(0x0e, INVALID);
|
||||
op(0x0f, INVALID);
|
||||
}
|
||||
#undef E
|
||||
|
||||
#define E (OP >> 21 & 15)
|
||||
#define DE (OP >> 11 & 7)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x00, VMULF<0>, VD, VS, VT, E);
|
||||
op(0x01, VMULF<1>, VD, VS, VT, E);
|
||||
op(0x02, VRND<1>, VD, VSn, VT, E);
|
||||
op(0x03, VMULQ, VD, VS, VT, E);
|
||||
op(0x04, VMUDL, VD, VS, VT, E);
|
||||
op(0x05, VMUDM, VD, VS, VT, E);
|
||||
op(0x06, VMUDN, VD, VS, VT, E);
|
||||
op(0x07, VMUDH, VD, VS, VT, E);
|
||||
op(0x08, VMACF<0>, VD, VS, VT, E);
|
||||
op(0x09, VMACF<1>, VD, VS, VT, E);
|
||||
op(0x0a, VRND<0>, VD, VSn, VT, E);
|
||||
op(0x0b, VMACQ, VD);
|
||||
op(0x0c, VMADL, VD, VS, VT, E);
|
||||
op(0x0d, VMADM, VD, VS, VT, E);
|
||||
op(0x0e, VMADN, VD, VS, VT, E);
|
||||
op(0x0f, VMADH, VD, VS, VT, E);
|
||||
op(0x10, VADD, VD, VS, VT, E);
|
||||
op(0x11, VSUB, VD, VS, VT, E);
|
||||
op(0x12, INVALID);
|
||||
op(0x13, VABS, VD, VS, VT, E);
|
||||
op(0x14, VADDC, VD, VS, VT, E);
|
||||
op(0x15, VSUBC, VD, VS, VT, E);
|
||||
op(0x16, INVALID);
|
||||
op(0x17, INVALID);
|
||||
op(0x18, INVALID);
|
||||
op(0x19, INVALID);
|
||||
op(0x1a, INVALID);
|
||||
op(0x1b, INVALID);
|
||||
op(0x1c, INVALID);
|
||||
op(0x1d, VSAR, VD, VS, E);
|
||||
op(0x1e, INVALID);
|
||||
op(0x1f, INVALID);
|
||||
op(0x20, VLT, VD, VS, VT, E);
|
||||
op(0x21, VEQ, VD, VS, VT, E);
|
||||
op(0x22, VNE, VD, VS, VT, E);
|
||||
op(0x23, VGE, VD, VS, VT, E);
|
||||
op(0x24, VCL, VD, VS, VT, E);
|
||||
op(0x25, VCH, VD, VS, VT, E);
|
||||
op(0x26, VCR, VD, VS, VT, E);
|
||||
op(0x27, VMRG, VD, VS, VT, E);
|
||||
op(0x28, VAND, VD, VS, VT, E);
|
||||
op(0x29, VNAND, VD, VS, VT, E);
|
||||
op(0x2a, VOR, VD, VS, VT, E);
|
||||
op(0x2b, VNOR, VD, VS, VT, E);
|
||||
op(0x2c, VXOR, VD, VS, VT, E);
|
||||
op(0x2d, VNXOR, VD, VS, VT, E);
|
||||
op(0x2e, INVALID);
|
||||
op(0x2f, INVALID);
|
||||
op(0x30, VRCP<0>, VD, DE, VT, E);
|
||||
op(0x31, VRCP<1>, VD, DE, VT, E);
|
||||
op(0x32, VRCPH, VD, DE, VT, E);
|
||||
op(0x33, VMOV, VD, DE, VT, E);
|
||||
op(0x34, VRSQ<0>, VD, DE, VT, E);
|
||||
op(0x35, VRSQ<1>, VD, DE, VT, E);
|
||||
op(0x36, VRSQH, VD, DE, VT, E);
|
||||
op(0x37, VNOP);
|
||||
op(0x38, INVALID);
|
||||
op(0x39, INVALID);
|
||||
op(0x3a, INVALID);
|
||||
op(0x3b, INVALID);
|
||||
op(0x3c, INVALID);
|
||||
op(0x3d, INVALID);
|
||||
op(0x3e, INVALID);
|
||||
op(0x3f, INVALID);
|
||||
}
|
||||
#undef E
|
||||
#undef DE
|
||||
}
|
||||
#undef DECODER_COP2
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_LWC2
|
||||
{
|
||||
#define E (OP >> 7 & 15)
|
||||
#define IMMi7 int7(OP)
|
||||
switch(OP >> 11 & 0x1f) {
|
||||
op(0x00, LBV, VT, E, RS, IMMi7);
|
||||
op(0x01, LSV, VT, E, RS, IMMi7);
|
||||
op(0x02, LLV, VT, E, RS, IMMi7);
|
||||
op(0x03, LDV, VT, E, RS, IMMi7);
|
||||
op(0x04, LQV, VT, E, RS, IMMi7);
|
||||
op(0x05, LRV, VT, E, RS, IMMi7);
|
||||
op(0x06, LPV, VT, E, RS, IMMi7);
|
||||
op(0x07, LUV, VT, E, RS, IMMi7);
|
||||
op(0x08, LHV, VT, E, RS, IMMi7);
|
||||
op(0x09, LFV, VT, E, RS, IMMi7);
|
||||
//op(0x0a, LWV, VT, E, RS, IMMi7); //not present on N64 RSP
|
||||
op(0x0b, LTV, VTn, E, RS, IMMi7);
|
||||
}
|
||||
#undef E
|
||||
#undef IMMi7
|
||||
}
|
||||
#undef DECODER_LWC2
|
||||
#endif
|
||||
|
||||
#ifdef DECODER_SWC2
|
||||
{
|
||||
#define E (OP >> 7 & 15)
|
||||
#define IMMi7 int7(OP)
|
||||
switch(OP >> 11 & 0x1f) {
|
||||
op(0x00, SBV, VT, E, RS, IMMi7);
|
||||
op(0x01, SSV, VT, E, RS, IMMi7);
|
||||
op(0x02, SLV, VT, E, RS, IMMi7);
|
||||
op(0x03, SDV, VT, E, RS, IMMi7);
|
||||
op(0x04, SQV, VT, E, RS, IMMi7);
|
||||
op(0x05, SRV, VT, E, RS, IMMi7);
|
||||
op(0x06, SPV, VT, E, RS, IMMi7);
|
||||
op(0x07, SUV, VT, E, RS, IMMi7);
|
||||
op(0x08, SHV, VT, E, RS, IMMi7);
|
||||
op(0x09, SFV, VT, E, RS, IMMi7);
|
||||
op(0x0a, SWV, VT, E, RS, IMMi7);
|
||||
op(0x0b, STV, VTn, E, RS, IMMi7);
|
||||
}
|
||||
#undef E
|
||||
#undef IMMi7
|
||||
}
|
||||
#undef DECODER_SWC2
|
||||
#endif
|
||||
|
||||
#undef SA
|
||||
#undef RDn
|
||||
#undef RTn
|
||||
#undef RSn
|
||||
#undef VDn
|
||||
#undef VSn
|
||||
#undef VTn
|
||||
#undef IMMi16
|
||||
#undef IMMu16
|
||||
#undef IMMu26
|
||||
@@ -365,10 +365,22 @@ auto RSP::Disassembler::VU() -> vector<string> {
|
||||
return {name, vdName(), vsValue(), vtValue()};
|
||||
};
|
||||
|
||||
auto DSE = [&](string_view name) -> vector<string> {
|
||||
static const string registerNames[] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"acch", "accm", "accl", "r11", "r12", "r13", "r14", "r15",
|
||||
};
|
||||
return {name, vdName(), vsValue(), registerNames[instruction >> 21 & 15]};
|
||||
};
|
||||
|
||||
auto DT = [&](string_view name) -> vector<string> {
|
||||
return {name, vmName(), vtValue()};
|
||||
};
|
||||
|
||||
auto D = [&](string_view name) -> vector<string> {
|
||||
return {name, vdName()};
|
||||
};
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return DST("vmulf");
|
||||
case 0x01: return DST("vmulu");
|
||||
@@ -381,7 +393,7 @@ auto RSP::Disassembler::VU() -> vector<string> {
|
||||
case 0x08: return DST("vmacf");
|
||||
case 0x09: return DST("vmacu");
|
||||
case 0x0a: return DST("vrndn");
|
||||
case 0x0b: return DST("vmacq");
|
||||
case 0x0b: return D("vmacq");
|
||||
case 0x0c: return DST("vmadl");
|
||||
case 0x0d: return DST("vmadm");
|
||||
case 0x0e: return DST("vmadn");
|
||||
@@ -399,7 +411,7 @@ auto RSP::Disassembler::VU() -> vector<string> {
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: return DST("vsar");
|
||||
case 0x1d: return DSE("vsar");
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return DST("vlt");
|
||||
@@ -479,9 +491,9 @@ auto RSP::Disassembler::sccRegisterName(uint index) const -> string {
|
||||
|
||||
auto RSP::Disassembler::sccRegisterValue(uint index) const -> string {
|
||||
u32 value = 0;
|
||||
if(index <= 6) value = rsp.readSCC((index & 7) << 2);
|
||||
if(index <= 6) value = rsp.readWord((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(index >= 8) value = rdp.readWord((index & 7) << 2);
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(value, 8L), "}")};
|
||||
return sccRegisterName(index);
|
||||
}
|
||||
|
||||
@@ -1,291 +0,0 @@
|
||||
auto RSP::instruction() -> void {
|
||||
pipeline.address = PC;
|
||||
pipeline.instruction = imem.readWord(pipeline.address);
|
||||
|
||||
debugger.instruction();
|
||||
//instructionDEBUG();
|
||||
instructionEXECUTE();
|
||||
GPR[0].u32 = 0;
|
||||
|
||||
switch(branch.state) {
|
||||
case Branch::Step: PC += 4; break;
|
||||
case Branch::DelaySlot: PC = branch.pc; branch.reset(); break;
|
||||
case Branch::Take: PC += 4; branch.delaySlot(); break;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionDEBUG() -> void {
|
||||
static uint counter = 0;
|
||||
//if(++counter > 100) return;
|
||||
print(
|
||||
disassembler.hint(hex(pipeline.address, 3L)), " ",
|
||||
//disassembler.hint(hex(pipeline.instruction, 8L)), " ",
|
||||
disassembler.disassemble(pipeline.address, pipeline.instruction), "\n"
|
||||
);
|
||||
}
|
||||
|
||||
auto RSP::instructionEXECUTE() -> void {
|
||||
switch(OP >> 26) {
|
||||
case 0x00: return instructionSPECIAL();
|
||||
case 0x01: return instructionREGIMM();
|
||||
case 0x02: return instructionJ();
|
||||
case 0x03: return instructionJAL();
|
||||
case 0x04: return instructionB(RS.u32 == RT.u32); //BEQ
|
||||
case 0x05: return instructionB(RS.u32 != RT.u32); //BNE
|
||||
case 0x06: return instructionB(RS.i32 <= 0); //BLEZ
|
||||
case 0x07: return instructionB(RS.i32 > 0); //BGTZ
|
||||
case 0x08: return instructionADDIU(); //ADDI
|
||||
case 0x09: return instructionADDIU();
|
||||
case 0x0a: return instructionSLTI();
|
||||
case 0x0b: return instructionSLTIU();
|
||||
case 0x0c: return instructionANDI();
|
||||
case 0x0d: return instructionORI();
|
||||
case 0x0e: return instructionXORI();
|
||||
case 0x0f: return instructionLUI();
|
||||
case 0x10: return instructionCOP0();
|
||||
case 0x11: break; //COP1
|
||||
case 0x12: return instructionCOP2();
|
||||
case 0x13: break; //COP3
|
||||
case 0x14: break; //BEQL
|
||||
case 0x15: break; //BNEL
|
||||
case 0x16: break; //BLEZL
|
||||
case 0x17: break; //BGTZL
|
||||
case 0x18: break; //DADDI
|
||||
case 0x19: break; //DADDIU
|
||||
case 0x1a: break; //LDL
|
||||
case 0x1b: break; //LDR
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return instructionLB();
|
||||
case 0x21: return instructionLH();
|
||||
case 0x22: break; //LWL
|
||||
case 0x23: return instructionLW();
|
||||
case 0x24: return instructionLBU();
|
||||
case 0x25: return instructionLHU();
|
||||
case 0x26: break; //LWR
|
||||
case 0x27: break; //LWU
|
||||
case 0x28: return instructionSB();
|
||||
case 0x29: return instructionSH();
|
||||
case 0x2a: break; //SWL
|
||||
case 0x2b: return instructionSW();
|
||||
case 0x2c: break; //SDL
|
||||
case 0x2d: break; //SDR
|
||||
case 0x2e: break; //SWR
|
||||
case 0x2f: return instructionCACHE();
|
||||
case 0x30: break; //LL
|
||||
case 0x31: break; //LWC1
|
||||
case 0x32: return instructionLWC2();
|
||||
case 0x33: break; //LWC3
|
||||
case 0x34: break; //LLD
|
||||
case 0x35: break; //LDC1
|
||||
case 0x36: break; //LDC2
|
||||
case 0x37: break; //LD
|
||||
case 0x38: break; //SC
|
||||
case 0x39: break; //SWC1
|
||||
case 0x3a: return instructionSWC2();
|
||||
case 0x3b: break; //SWC3
|
||||
case 0x3c: break; //SCD
|
||||
case 0x3d: break; //SDC1
|
||||
case 0x3e: break; //SDC2
|
||||
case 0x3f: break; //SD
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionSPECIAL() -> void {
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionSLL();
|
||||
case 0x01: break;
|
||||
case 0x02: return instructionSRL();
|
||||
case 0x03: return instructionSRA();
|
||||
case 0x04: return instructionSLLV();
|
||||
case 0x05: break;
|
||||
case 0x06: return instructionSRLV();
|
||||
case 0x07: return instructionSRAV();
|
||||
case 0x08: return instructionJR();
|
||||
case 0x09: return instructionJALR();
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: break; //SYSCALL
|
||||
case 0x0d: return instructionBREAK();
|
||||
case 0x0e: break;
|
||||
case 0x0f: break; //SYNC
|
||||
case 0x10: break; //MFHI
|
||||
case 0x11: break; //MTHI
|
||||
case 0x12: break; //MFLO
|
||||
case 0x13: break; //MTLO
|
||||
case 0x14: break; //DSLLV
|
||||
case 0x15: break;
|
||||
case 0x16: break; //DSRLV
|
||||
case 0x17: break; //DSRAV
|
||||
case 0x18: break; //MULT
|
||||
case 0x19: break; //MULTU
|
||||
case 0x1a: break; //DIV
|
||||
case 0x1b: break; //DIVU
|
||||
case 0x1c: break; //DMULT
|
||||
case 0x1d: break; //DMULTU
|
||||
case 0x1e: break; //DDIV
|
||||
case 0x1f: break; //DDIVU
|
||||
case 0x20: return instructionADDU(); //ADD
|
||||
case 0x21: return instructionADDU();
|
||||
case 0x22: return instructionSUBU(); //SUB
|
||||
case 0x23: return instructionSUBU();
|
||||
case 0x24: return instructionAND();
|
||||
case 0x25: return instructionOR();
|
||||
case 0x26: return instructionXOR();
|
||||
case 0x27: return instructionNOR();
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return instructionSLT();
|
||||
case 0x2b: return instructionSLTU();
|
||||
case 0x2c: break; //DADD
|
||||
case 0x2d: break; //DADDU
|
||||
case 0x2e: break; //DSUB
|
||||
case 0x2f: break; //DSUBU
|
||||
case 0x30: break; //TGE
|
||||
case 0x31: break; //TGEU
|
||||
case 0x32: break; //TLT
|
||||
case 0x33: break; //TLTU
|
||||
case 0x34: break; //TEQ
|
||||
case 0x35: break;
|
||||
case 0x36: break; //TNE
|
||||
case 0x37: break;
|
||||
case 0x38: break; //DSLL
|
||||
case 0x39: break;
|
||||
case 0x3a: break; //DSRL
|
||||
case 0x3b: break; //DSRA
|
||||
case 0x3c: break; //DSLL32
|
||||
case 0x3d: break;
|
||||
case 0x3e: break; //DSRL32
|
||||
case 0x3f: break; //DSRA32
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionREGIMM() -> void {
|
||||
switch(OP >> 16 & 0x1f) {
|
||||
case 0x00: return instructionB(RS.i32 < 0); //BLTZ
|
||||
case 0x01: return instructionB(RS.i32 >= 0); //BGEZ
|
||||
case 0x02: break; //BLTZL
|
||||
case 0x03: break; //BGEZL
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: break; //TGEI
|
||||
case 0x09: break; //TGEIU
|
||||
case 0x0a: break; //TLTI
|
||||
case 0x0b: break; //TLTIU
|
||||
case 0x0c: break; //TEQI
|
||||
case 0x0d: break;
|
||||
case 0x0e: break; //TNEI
|
||||
case 0x0f: break;
|
||||
case 0x10: return instructionBAL(RS.i32 < 0); //BLTZAL
|
||||
case 0x11: return instructionBAL(RS.i32 >= 0); //BGEZAL
|
||||
case 0x12: break; //BLTZALL
|
||||
case 0x13: break; //BGEZALL
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionCOP0() -> void {
|
||||
switch(OP >> 21 & 31) {
|
||||
case 0x00: return instructionMFC0();
|
||||
case 0x01: break; //DMFC0
|
||||
case 0x02: break; //CFC0
|
||||
case 0x04: return instructionMTC0();
|
||||
case 0x05: break; //DMTC0
|
||||
case 0x06: break; //CTC0
|
||||
case 0x08: break; //BC0
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::instructionCOP2() -> void {
|
||||
switch(OP >> 21 & 31) {
|
||||
case 0x00: return instructionMFC2();
|
||||
case 0x01: break; //DMFC2
|
||||
case 0x02: return instructionCFC2();
|
||||
case 0x04: return instructionMTC2();
|
||||
case 0x05: break; //DMTC2
|
||||
case 0x06: return instructionCTC2();
|
||||
case 0x08: break; //BC2
|
||||
}
|
||||
if(!(OP >> 25 & 1)) return;
|
||||
switch(OP & 0x3f) {
|
||||
case 0x00: return instructionVMULF(0);
|
||||
case 0x01: return instructionVMULF(1);
|
||||
case 0x02: return instructionVRND(1);
|
||||
case 0x03: return instructionVMULQ();
|
||||
case 0x04: return instructionVMUDL();
|
||||
case 0x05: return instructionVMUDM();
|
||||
case 0x06: return instructionVMUDN();
|
||||
case 0x07: return instructionVMUDH();
|
||||
case 0x08: return instructionVMACF(0);
|
||||
case 0x09: return instructionVMACF(1);
|
||||
case 0x0a: return instructionVRND(0);
|
||||
case 0x0b: return instructionVMACQ();
|
||||
case 0x0c: return instructionVMADL();
|
||||
case 0x0d: return instructionVMADM();
|
||||
case 0x0e: return instructionVMADN();
|
||||
case 0x0f: return instructionVMADH();
|
||||
case 0x10: return instructionVADD();
|
||||
case 0x11: return instructionVSUB();
|
||||
case 0x12: break;
|
||||
case 0x13: return instructionVABS();
|
||||
case 0x14: return instructionVADDC();
|
||||
case 0x15: return instructionVSUBC();
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: return instructionVSAR();
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return instructionVLT();
|
||||
case 0x21: return instructionVEQ();
|
||||
case 0x22: return instructionVNE();
|
||||
case 0x23: return instructionVGE();
|
||||
case 0x24: return instructionVCL();
|
||||
case 0x25: return instructionVCH();
|
||||
case 0x26: return instructionVCR();
|
||||
case 0x27: return instructionVMRG();
|
||||
case 0x28: return instructionVAND();
|
||||
case 0x29: return instructionVNAND();
|
||||
case 0x2a: return instructionVOR();
|
||||
case 0x2b: return instructionVNOR();
|
||||
case 0x2c: return instructionVXOR();
|
||||
case 0x2d: return instructionVNXOR();
|
||||
case 0x2e: break;
|
||||
case 0x2f: break;
|
||||
case 0x30: return instructionVRCP(0);
|
||||
case 0x31: return instructionVRCP(1);
|
||||
case 0x32: return instructionVRCPH();
|
||||
case 0x33: return instructionVMOV();
|
||||
case 0x34: return instructionVRSQ(0);
|
||||
case 0x35: return instructionVRSQ(1);
|
||||
case 0x36: return instructionVRSQH();
|
||||
case 0x37: return instructionVNOP();
|
||||
case 0x38: break;
|
||||
case 0x39: break;
|
||||
case 0x3a: break;
|
||||
case 0x3b: break;
|
||||
case 0x3c: break;
|
||||
case 0x3d: break;
|
||||
case 0x3e: break;
|
||||
case 0x3f: break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
auto RSP::Recompiler::pool() -> Pool* {
|
||||
if(context) return context;
|
||||
|
||||
context = (Pool*)allocator.acquire();
|
||||
u32 hashcode = 0;
|
||||
for(u32 offset : range(4096)) {
|
||||
hashcode = (hashcode << 5) + hashcode + self.imem.readByte(offset);
|
||||
}
|
||||
context->hashcode = hashcode;
|
||||
|
||||
if(auto result = pools.find(*context)) {
|
||||
context->hashcode = 0; //leave the memory zeroed out
|
||||
return context = &result();
|
||||
}
|
||||
|
||||
allocator.reserve(sizeof(Pool));
|
||||
if(auto result = pools.insert(*context)) {
|
||||
return context = &result();
|
||||
}
|
||||
|
||||
throw; //should never occur
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::block(u32 address) -> Block* {
|
||||
auto& block = pool()->blocks[address >> 2 & 0x3ff];
|
||||
if(!block) block = emit(address);
|
||||
return block;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emit(u32 address) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("RSP allocator flush\n");
|
||||
allocator.release(bump_allocator::zero_fill);
|
||||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
block->code = allocator.acquire();
|
||||
bind({block->code, allocator.available()});
|
||||
|
||||
bool branched = 0;
|
||||
uint instructions = 0;
|
||||
do {
|
||||
u32 instruction = self.imem.readWord(address);
|
||||
branched = emitEXECUTE(instruction);
|
||||
call(&RSP::instructionEpilogue, &self);
|
||||
test(rax, rax);
|
||||
jz(imm8{+1});
|
||||
ret();
|
||||
address += 4;
|
||||
if((address & 0xffc) == 0) break; //IMEM boundary
|
||||
} while(++instructions < 64 && !branched);
|
||||
ret();
|
||||
|
||||
block->step = 3 * instructions;
|
||||
allocator.reserve(size());
|
||||
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
|
||||
return block;
|
||||
}
|
||||
|
||||
#define OP instruction
|
||||
#define RD &self.GPR[RDn]
|
||||
#define RT &self.GPR[RTn]
|
||||
#define RS &self.GPR[RSn]
|
||||
#define VD &self.VPR[VDn]
|
||||
#define VS &self.VPR[VSn]
|
||||
#define VT &self.VPR[VTn]
|
||||
|
||||
#define jp(id, name) \
|
||||
case id: \
|
||||
return emit##name(instruction); \
|
||||
|
||||
#define op(id, name, ...) \
|
||||
case id: \
|
||||
call(&RSP::instruction##name, &self, ##__VA_ARGS__); \
|
||||
return 0; \
|
||||
|
||||
#define br(id, name, ...) \
|
||||
case id: \
|
||||
call(&RSP::instruction##name, &self, ##__VA_ARGS__); \
|
||||
return 1; \
|
||||
|
||||
auto RSP::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
||||
#define DECODER_EXECUTE
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
||||
#define DECODER_SPECIAL
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emitREGIMM(u32 instruction) -> bool {
|
||||
#define DECODER_REGIMM
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emitCOP0(u32 instruction) -> bool {
|
||||
#define DECODER_COP0
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emitCOP2(u32 instruction) -> bool {
|
||||
#define DECODER_COP2
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emitLWC2(u32 instruction) -> bool {
|
||||
#define DECODER_LWC2
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emitSWC2(u32 instruction) -> bool {
|
||||
#define DECODER_SWC2
|
||||
#include "decoder.hpp"
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef jp
|
||||
#undef op
|
||||
#undef br
|
||||
|
||||
#undef OP
|
||||
#undef RD
|
||||
#undef RT
|
||||
#undef RS
|
||||
#undef VD
|
||||
#undef VS
|
||||
#undef VT
|
||||
@@ -0,0 +1,50 @@
|
||||
//{
|
||||
struct Recompiler : recompiler::amd64 {
|
||||
RSP& self;
|
||||
Recompiler(RSP& self) : self(self) {}
|
||||
|
||||
struct Block {
|
||||
auto execute() -> void {
|
||||
((void (*)())code)();
|
||||
}
|
||||
|
||||
u32 step;
|
||||
u8* code;
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
auto operator==(const Pool& source) const -> bool { return hashcode == source.hashcode; }
|
||||
auto operator< (const Pool& source) const -> bool { return hashcode < source.hashcode; }
|
||||
auto hash() const -> u32 { return hashcode; }
|
||||
|
||||
u32 hashcode;
|
||||
Block* blocks[1024];
|
||||
};
|
||||
|
||||
auto reset() -> void {
|
||||
context = nullptr;
|
||||
pools.reset();
|
||||
}
|
||||
|
||||
auto invalidate() -> void {
|
||||
context = nullptr;
|
||||
}
|
||||
|
||||
auto pool() -> Pool*;
|
||||
auto block(u32 address) -> Block*;
|
||||
|
||||
auto emit(u32 address) -> Block*;
|
||||
auto emitEXECUTE(u32 instruction) -> bool;
|
||||
auto emitSPECIAL(u32 instruction) -> bool;
|
||||
auto emitREGIMM(u32 instruction) -> bool;
|
||||
auto emitCOP0(u32 instruction) -> bool;
|
||||
auto emitCOP2(u32 instruction) -> bool;
|
||||
auto emitLWC2(u32 instruction) -> bool;
|
||||
auto emitSWC2(u32 instruction) -> bool;
|
||||
|
||||
bump_allocator allocator;
|
||||
Pool* context = nullptr;
|
||||
set<Pool> pools;
|
||||
//hashset<Pool> pools;
|
||||
} recompiler{*this};
|
||||
//};
|
||||
@@ -1,37 +1,13 @@
|
||||
#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
|
||||
@@ -48,37 +24,13 @@
|
||||
#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
|
||||
|
||||
@@ -1,9 +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::instructionMFC0(r32& rt, u8 rd) -> void {
|
||||
if((rd & 8) == 0) rt.u32 = Nintendo64::rsp.readWord((rd & 7) << 2);
|
||||
if((rd & 8) != 0) rt.u32 = Nintendo64::rdp.readWord((rd & 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);
|
||||
auto RSP::instructionMTC0(cr32& rt, u8 rd) -> void {
|
||||
if((rd & 8) == 0) Nintendo64::rsp.writeWord((rd & 7) << 2, rt.u32);
|
||||
if((rd & 8) != 0) Nintendo64::rdp.writeWord((rd & 7) << 2, rt.u32);
|
||||
}
|
||||
@@ -4,6 +4,6 @@
|
||||
auto setControlRegister(uint, uint32) -> void;
|
||||
|
||||
//scc-instructions.cpp
|
||||
auto instructionMFC0() -> void;
|
||||
auto instructionMTC0() -> void;
|
||||
auto instructionMFC0(r32& rt, u8 rd) -> void;
|
||||
auto instructionMTC0(cr32& rt, u8 rd) -> void;
|
||||
//};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,72 @@
|
||||
auto RSP::r128::operator()(uint index) const -> r128 {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
r128 v{*this};
|
||||
switch(index) {
|
||||
case 0: break;
|
||||
case 1: break;
|
||||
case 2: v.u16(1) = v.u16(0); v.u16(3) = v.u16(2); v.u16(5) = v.u16(4); v.u16(7) = v.u16(6); break;
|
||||
case 3: v.u16(0) = v.u16(1); v.u16(2) = v.u16(3); v.u16(4) = v.u16(5); v.u16(6) = v.u16(7); break;
|
||||
case 4: v.u16(1) = v.u16(2) = v.u16(3) = v.u16(0); v.u16(5) = v.u16(6) = v.u16(7) = v.u16(4); break;
|
||||
case 5: v.u16(0) = v.u16(2) = v.u16(3) = v.u16(1); v.u16(4) = v.u16(6) = v.u16(7) = v.u16(5); break;
|
||||
case 6: v.u16(0) = v.u16(1) = v.u16(3) = v.u16(2); v.u16(4) = v.u16(5) = v.u16(7) = v.u16(6); break;
|
||||
case 7: v.u16(0) = v.u16(1) = v.u16(2) = v.u16(3); v.u16(4) = v.u16(5) = v.u16(6) = v.u16(7); break;
|
||||
case 8: for(uint n : range(8)) v.u16(n) = v.u16(0); break;
|
||||
case 9: for(uint n : range(8)) v.u16(n) = v.u16(1); break;
|
||||
case 10: for(uint n : range(8)) v.u16(n) = v.u16(2); break;
|
||||
case 11: for(uint n : range(8)) v.u16(n) = v.u16(3); break;
|
||||
case 12: for(uint n : range(8)) v.u16(n) = v.u16(4); break;
|
||||
case 13: for(uint n : range(8)) v.u16(n) = v.u16(5); break;
|
||||
case 14: for(uint n : range(8)) v.u16(n) = v.u16(6); break;
|
||||
case 15: for(uint n : range(8)) v.u16(n) = v.u16(7); break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
static const __m128i shuffle[16] = {
|
||||
//vector
|
||||
_mm_set_epi8(15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567
|
||||
_mm_set_epi8(15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567
|
||||
//scalar quarter
|
||||
_mm_set_epi8(15,14,15,14,11,10,11,10, 7, 6, 7, 6, 3, 2, 3, 2), //00224466
|
||||
_mm_set_epi8(13,12,13,12, 9, 8, 9, 8, 5, 4, 5, 4, 1, 0, 1, 0), //11335577
|
||||
//scalar half
|
||||
_mm_set_epi8(15,14,15,14,15,14,15,14, 7, 6, 7, 6, 7, 6, 7, 6), //00004444
|
||||
_mm_set_epi8(13,12,13,12,13,12,13,12, 5, 4, 5, 4, 5, 4, 5, 4), //11115555
|
||||
_mm_set_epi8(11,10,11,10,11,10,11,10, 3, 2, 3, 2, 3, 2, 3, 2), //22226666
|
||||
_mm_set_epi8( 9, 8, 9, 8, 9, 8, 9, 8, 1, 0, 1, 0, 1, 0, 1, 0), //33337777
|
||||
//scalar whole
|
||||
_mm_set_epi8(15,14,15,14,15,14,15,14,15,14,15,14,15,14,15,14), //00000000
|
||||
_mm_set_epi8(13,12,13,12,13,12,13,12,13,12,13,12,13,12,13,12), //11111111
|
||||
_mm_set_epi8(11,10,11,10,11,10,11,10,11,10,11,10,11,10,11,10), //22222222
|
||||
_mm_set_epi8( 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8), //33333333
|
||||
_mm_set_epi8( 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6), //44444444
|
||||
_mm_set_epi8( 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4), //55555555
|
||||
_mm_set_epi8( 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2), //66666666
|
||||
_mm_set_epi8( 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0), //77777777
|
||||
};
|
||||
//todo: benchmark to see if testing for cases 0&1 to return value directly is faster
|
||||
return {uint128_t(_mm_shuffle_epi8(v128, shuffle[index]))};
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::accumulatorGet(uint index) const -> u64 {
|
||||
return (u64)ACCH.u16(index) << 32 | (u64)ACCM.u16(index) << 16 | (u64)ACCL.u16(index) << 0;
|
||||
}
|
||||
|
||||
auto RSP::accumulatorSet(uint index, u64 value) -> void {
|
||||
ACCH.u16(index) = value >> 32;
|
||||
ACCM.u16(index) = value >> 16;
|
||||
ACCL.u16(index) = value >> 0;
|
||||
}
|
||||
|
||||
auto RSP::accumulatorSaturate(uint index, bool slice, u16 negative, u16 positive) const -> u16 {
|
||||
if(ACCH.i16(index) < 0) {
|
||||
if(ACCH.u16(index) != 0xffff) return negative;
|
||||
if(ACCM.i16(index) >= 0) return negative;
|
||||
} else {
|
||||
if(ACCH.u16(index) != 0x0000) return positive;
|
||||
if(ACCM.i16(index) < 0) return positive;
|
||||
}
|
||||
return !slice ? ACCL.u16(index) : ACCM.u16(index);
|
||||
}
|
||||
+111
-84
@@ -1,101 +1,128 @@
|
||||
//{
|
||||
union v128 {
|
||||
union r128 {
|
||||
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]; }
|
||||
};
|
||||
struct { __m128i v128; };
|
||||
|
||||
//vu-instructions.cpp
|
||||
auto vte() -> v128;
|
||||
operator __m128i() const { return v128; }
|
||||
auto operator=(__m128i value) { v128 = value; }
|
||||
|
||||
auto byte(uint index) -> uint8_t& { return ((uint8_t*)&u128)[15 - index]; }
|
||||
auto byte(uint index) const -> uint8_t { return ((uint8_t*)&u128)[15 - index]; }
|
||||
|
||||
auto element(uint index) -> uint16_t& { return ((uint16_t*)&u128)[7 - index]; }
|
||||
auto element(uint index) const -> uint16_t { return ((uint16_t*)&u128)[7 - index]; }
|
||||
|
||||
auto u8(uint index) -> uint8_t& { return ((uint8_t*)&u128)[15 - index]; }
|
||||
auto u8(uint index) const -> uint8_t { return ((uint8_t*)&u128)[15 - index]; }
|
||||
|
||||
auto i16(uint index) -> int16_t& { return ((int16_t*)&u128)[7 - index]; }
|
||||
auto i16(uint index) const -> int16_t { return ((int16_t*)&u128)[7 - index]; }
|
||||
|
||||
auto u16(uint index) -> uint16_t& { return ((uint16_t*)&u128)[7 - index]; }
|
||||
auto u16(uint index) const -> uint16_t { return ((uint16_t*)&u128)[7 - index]; }
|
||||
|
||||
//VCx registers
|
||||
auto get(uint index) const -> bool { return u16(index) != 0; }
|
||||
auto set(uint index, bool value) -> bool { return u16(index) = 0 - value, value; }
|
||||
|
||||
//vu-registers.cpp
|
||||
auto operator()(uint index) const -> r128;
|
||||
};
|
||||
using cr128 = const r128;
|
||||
|
||||
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
|
||||
r128 r[32];
|
||||
r128 acch, accm, accl;
|
||||
r128 vcoh, vcol; //16-bit little endian
|
||||
r128 vcch, vccl; //16-bit little endian
|
||||
r128 vce; // 8-bit little endian
|
||||
i16 divin;
|
||||
i16 divout;
|
||||
bool divdp;
|
||||
} vu;
|
||||
|
||||
//vu-registers.cpp
|
||||
auto accumulatorGet(uint index) const -> u64;
|
||||
auto accumulatorSet(uint index, u64 value) -> void;
|
||||
auto accumulatorSaturate(uint index, bool slice, u16 negative, u16 positive) const -> u16;
|
||||
|
||||
u16 reciprocals[512];
|
||||
u16 inverseSquareRoots[512];
|
||||
|
||||
static constexpr v128 zero{0};
|
||||
static constexpr v128 invert{u128(0) - 1};
|
||||
static constexpr r128 zero{0};
|
||||
static constexpr r128 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 instructionCFC2(r32& rt, u8 rd) -> void;
|
||||
auto instructionCTC2(cr32& rt, u8 rd) -> void;
|
||||
auto instructionLBV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLDV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLFV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLHV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLLV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLPV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLQV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLRV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLSV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLTV(u8 vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLUV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionLWV(r128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionMFC2(r32& rt, cr128& vs, u8 e) -> void;
|
||||
auto instructionMTC2(cr32& rt, r128& vs, u8 e) -> void;
|
||||
auto instructionSBV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSDV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSFV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSHV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSLV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSPV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSQV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSRV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSSV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSTV(u8 vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSUV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionSWV(cr128& vt, u8 e, cr32& rs, i8 imm) -> void;
|
||||
auto instructionVABS(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVADD(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVADDC(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVAND(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVCH(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVCL(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVCR(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVEQ(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVGE(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVLT(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
template<bool U>
|
||||
auto instructionVMACF(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMACQ(r128& vd) -> void;
|
||||
auto instructionVMADH(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMADL(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMADM(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMADN(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMOV(r128& vd, u8 de, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMRG(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMUDH(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMUDL(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMUDM(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMUDN(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
template<bool U>
|
||||
auto instructionVMULF(r128& rd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVMULQ(r128& rd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVNAND(r128& rd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVNE(r128& vd, cr128& vs, cr128& vt, u8 e) -> 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;
|
||||
auto instructionVNOR(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVNXOR(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVOR(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
template<bool L>
|
||||
auto instructionVRCP(r128& vd, u8 de, cr128& vt, u8 e) -> void;
|
||||
auto instructionVRCPH(r128& vd, u8 de, cr128& vt, u8 e) -> void;
|
||||
template<bool D>
|
||||
auto instructionVRND(r128& vd, u8 vs, cr128& vt, u8 e) -> void;
|
||||
template<bool L>
|
||||
auto instructionVRSQ(r128& vd, u8 de, cr128& vt, u8 e) -> void;
|
||||
auto instructionVRSQH(r128& vd, u8 de, cr128& vt, u8 e) -> void;
|
||||
auto instructionVSAR(r128& vd, cr128& vs, u8 e) -> void;
|
||||
auto instructionVSUB(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVSUBC(r128& vd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
auto instructionVXOR(r128& rd, cr128& vs, cr128& vt, u8 e) -> void;
|
||||
//};
|
||||
@@ -1,3 +1,5 @@
|
||||
#define rsp Nintendo64::rsp
|
||||
|
||||
auto RSP::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Instruction>("Instruction", "RSP");
|
||||
tracer.instruction->setAddressBits(12);
|
||||
@@ -40,3 +42,5 @@ auto RSP::Debugger::io(string_view message) -> void {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
|
||||
#undef rsp
|
||||
@@ -1,177 +0,0 @@
|
||||
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)});
|
||||
}
|
||||
}
|
||||
+191
-11
@@ -1,36 +1,216 @@
|
||||
static const vector<string> registerNames = {
|
||||
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::readWord(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::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0x3ffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
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 target = dma.memSource == 0 ? 0x0400'0000 : 0x0400'1000;
|
||||
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 = bus.readWord(dma.dramAddress + offset);
|
||||
bus.writeWord(dma.memAddress + target + 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 source = dma.memSource == 0 ? 0x0400'0000 : 0x0400'1000;
|
||||
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 = bus.readWord(dma.memAddress + source + offset);
|
||||
bus.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)});
|
||||
}
|
||||
}
|
||||
|
||||
static const vector<string> registerNamesIO = {
|
||||
"SP_PC_REG",
|
||||
"SP_IBIST",
|
||||
};
|
||||
|
||||
auto RSP::readIO(u32 address) -> u32 {
|
||||
auto RSP::Status::readWord(u32 address) -> u32 {
|
||||
address = (address & 0x7ffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PC_REG
|
||||
data.bit(0,11) = core.pc;
|
||||
data.bit(0,11) = self.core.pc;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//SP_IBIST
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "SP_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
if(self.debugger.tracer.io->enabled()) {
|
||||
self.debugger.io({registerNamesIO(address, "SP_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto RSP::writeIO(u32 address, uint32 data) -> void {
|
||||
auto RSP::Status::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0x7ffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//SP_PC_REG
|
||||
if(branch.state == Branch::Take) {
|
||||
branch.pc = data.bit(0,11) & ~3;
|
||||
if(self.branch.state == Branch::Take) {
|
||||
self.branch.pc = data.bit(0,11) & ~3;
|
||||
} else {
|
||||
core.pc = data.bit(0,11) & ~3;
|
||||
self.core.pc = data.bit(0,11) & ~3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +218,7 @@ auto RSP::writeIO(u32 address, uint32 data) -> void {
|
||||
//SP_IBIST
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "SP_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
if(self.debugger.tracer.io->enabled()) {
|
||||
self.debugger.io({registerNamesIO(address, "SP_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace ares::Nintendo64 {
|
||||
RSP rsp;
|
||||
#include "core/core.cpp"
|
||||
#include "io.cpp"
|
||||
#include "io-scc.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
@@ -26,7 +25,6 @@ auto RSP::unload() -> void {
|
||||
auto RSP::main() -> void {
|
||||
if(status.halted) return step(96);
|
||||
instruction();
|
||||
step(3);
|
||||
}
|
||||
|
||||
auto RSP::step(uint clocks) -> void {
|
||||
@@ -37,7 +35,13 @@ auto RSP::power() -> void {
|
||||
Thread::reset();
|
||||
powerCore();
|
||||
dma = {};
|
||||
status = {};
|
||||
status.semaphore = 0;
|
||||
status.halted = 1;
|
||||
status.broken = 0;
|
||||
status.full = 0;
|
||||
status.singleStep = 0;
|
||||
status.interruptOnBreak = 0;
|
||||
for(auto& signal : status.signal) signal = 0;
|
||||
}
|
||||
|
||||
}
|
||||
+21
-18
@@ -1,9 +1,9 @@
|
||||
//Reality Signal Processor
|
||||
|
||||
struct RSP : Thread {
|
||||
struct RSP : Thread, Memory::IO<RSP> {
|
||||
Node::Component node;
|
||||
Memory dmem;
|
||||
Memory imem;
|
||||
Memory::Writable dmem;
|
||||
Memory::Writable imem;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
@@ -32,12 +32,8 @@ 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;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@@ -56,15 +52,22 @@ struct RSP : Thread {
|
||||
} 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;
|
||||
struct Status : Memory::IO<Status> {
|
||||
RSP& self;
|
||||
Status(RSP& self) : self(self) {}
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
uint1 semaphore = 0;
|
||||
uint1 halted = 1;
|
||||
uint1 broken = 0;
|
||||
uint1 full = 0;
|
||||
uint1 singleStep = 0;
|
||||
uint1 interruptOnBreak = 0;
|
||||
uint1 signal[8] = {};
|
||||
} status{*this};
|
||||
|
||||
#include "core/core.hpp"
|
||||
};
|
||||
|
||||
+3
-2
@@ -8,7 +8,7 @@ static const vector<string> registerNames = {
|
||||
"SI_STATUS",
|
||||
};
|
||||
|
||||
auto SI::readIO(u32 address) -> u32 {
|
||||
auto SI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
@@ -56,8 +56,9 @@ auto SI::readIO(u32 address) -> u32 {
|
||||
return data;
|
||||
}
|
||||
|
||||
auto SI::writeIO(u32 address, uint32 data) -> void {
|
||||
auto SI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//SI_DRAM_ADDRESS
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
//Serial Interface
|
||||
|
||||
struct SI {
|
||||
struct SI : Memory::IO<SI> {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
@@ -20,8 +20,8 @@ struct SI {
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(u32 address) -> u32;
|
||||
auto writeIO(u32 address, uint32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
@@ -60,6 +60,7 @@ auto System::serializeAll(serializer& s, bool synchronize) -> void {
|
||||
controllerPort2.serialize(s);
|
||||
controllerPort3.serialize(s);
|
||||
controllerPort4.serialize(s);
|
||||
dd.serialize(s);
|
||||
}
|
||||
|
||||
auto System::serializeInit(bool synchronize) -> uint {
|
||||
|
||||
@@ -43,6 +43,7 @@ auto System::load(Node::Object& root) -> void {
|
||||
controllerPort2.load(node);
|
||||
controllerPort3.load(node);
|
||||
controllerPort4.load(node);
|
||||
dd.load(node);
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
@@ -63,6 +64,7 @@ auto System::unload() -> void {
|
||||
controllerPort2.unload();
|
||||
controllerPort3.unload();
|
||||
controllerPort4.unload();
|
||||
dd.unload();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
@@ -89,6 +91,7 @@ auto System::power(bool reset) -> void {
|
||||
}
|
||||
|
||||
cartridge.power();
|
||||
dd.power(reset);
|
||||
mi.power();
|
||||
vi.power();
|
||||
ai.power();
|
||||
|
||||
Loaded 100 of 275 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user