mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-11 01:11:42 -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 | ||
|
|
842ea44b90 | ||
|
|
e67dfac166 | ||
|
|
a5d2ee5e5f | ||
|
|
bef70d2550 | ||
|
|
f656e317c8 | ||
|
|
494b1cb0ba | ||
|
|
be25bb110c | ||
|
|
1f59ef7457 | ||
|
|
74e6d389a6 | ||
|
|
9449b78675 | ||
|
|
388f555304 | ||
|
|
4cf677063c | ||
|
|
890b56f84d | ||
|
|
ddd3a439ff | ||
|
|
318beb509f | ||
|
|
6f6e2ea260 | ||
|
|
67cf63b532 | ||
|
|
a427fe2a71 | ||
|
|
e6c42199aa | ||
|
|
b224caf4bd | ||
|
|
bd565eb982 | ||
|
|
f149b5d2a5 | ||
|
|
32d8abd372 | ||
|
|
5ef188c876 | ||
|
|
590773508e | ||
|
|
2f24221be9 | ||
|
|
e7b120f2e5 | ||
|
|
4a1084179b | ||
|
|
e9bd3dda68 | ||
|
|
c1f9897a03 | ||
|
|
291a0ea8de |
No files matched your search
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
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 = "112";
|
||||
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";
|
||||
}
|
||||
@@ -5,6 +5,7 @@ struct Port : Object {
|
||||
auto type() const -> string { return _type; }
|
||||
auto family() const -> string { return _family; }
|
||||
auto hotSwappable() const -> bool { return _hotSwappable; }
|
||||
auto supported() const -> vector<string> { return _supported; }
|
||||
|
||||
auto setAllocate(function<Node::Peripheral (string)> allocate) -> void { _allocate = allocate; }
|
||||
auto setConnect(function<void ()> connect) -> void { _connect = connect; }
|
||||
@@ -12,6 +13,7 @@ struct Port : Object {
|
||||
auto setType(string type) -> void { _type = type; }
|
||||
auto setFamily(string family) -> void { _family = family; }
|
||||
auto setHotSwappable(bool hotSwappable) -> void { _hotSwappable = hotSwappable; }
|
||||
auto setSupported(vector<string> supported) -> void { _supported = supported; }
|
||||
|
||||
auto connected() -> Node::Peripheral {
|
||||
return find<Node::Peripheral>(0);
|
||||
@@ -68,4 +70,5 @@ protected:
|
||||
string _type;
|
||||
string _family;
|
||||
bool _hotSwappable = false;
|
||||
vector<string> _supported;
|
||||
};
|
||||
@@ -1,3 +1,18 @@
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using i128 = int128_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using u128 = uint128_t;
|
||||
|
||||
using f32 = float32_t;
|
||||
using f64 = float64_t;
|
||||
|
||||
using int1 = nall::Integer< 1>;
|
||||
using int2 = nall::Integer< 2>;
|
||||
using int3 = nall::Integer< 3>;
|
||||
|
||||
@@ -21,7 +21,6 @@ ares.objects += $(if $(findstring spc700,$(ares.components)),ares-processor-spc7
|
||||
ares.objects += $(if $(findstring tlcs900h,$(ares.components)),ares-processor-tlcs900h)
|
||||
ares.objects += $(if $(findstring upd96050,$(ares.components)),ares-processor-upd96050)
|
||||
ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz)
|
||||
ares.objects += $(if $(findstring vr4300,$(ares.components)),ares-processor-vr4300)
|
||||
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
|
||||
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
|
||||
|
||||
@@ -49,7 +48,6 @@ $(object.path)/ares-processor-spc700.o: $(ares.path)/component/processor/spc70
|
||||
$(object.path)/ares-processor-tlcs900h.o: $(ares.path)/component/processor/tlcs900h/tlcs900h.cpp
|
||||
$(object.path)/ares-processor-upd96050.o: $(ares.path)/component/processor/upd96050/upd96050.cpp
|
||||
$(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz/v30mz.cpp
|
||||
$(object.path)/ares-processor-vr4300.o: $(ares.path)/component/processor/vr4300/vr4300.cpp
|
||||
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
|
||||
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
auto VR4300::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "vr4300.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto VR4300::power() -> void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//NEC VR4300
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct VR4300 {
|
||||
//vr4300.cpp
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
}
|
||||
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"
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -11,6 +11,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Control Pad", "Fighting Pad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -13,6 +13,7 @@ auto Keyboard::load(Node::Object parent) -> void {
|
||||
port->setAllocate([&](auto name) { return allocate(port, name); });
|
||||
port->setConnect([&] { connect(); });
|
||||
port->setDisconnect([&] { disconnect(); });
|
||||
port->setSupported({"Japanese"});
|
||||
}
|
||||
|
||||
auto Keyboard::unload() -> void {
|
||||
|
||||
+19
-5
@@ -1,13 +1,27 @@
|
||||
ares.components += vr4300
|
||||
|
||||
ares.objects += ares-n64-interface ares-n64-system
|
||||
ares.objects += ares-n64-cartridge ares-n64-controller
|
||||
ares.objects += ares-n64-cpu ares-n64-rdp ares-n64-rsp
|
||||
ares.objects += ares-n64-interface ares-n64-memory ares-n64-system
|
||||
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
|
||||
|
||||
$(object.path)/ares-n64-interface.o: $(ares.path)/n64/interface/interface.cpp
|
||||
$(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp
|
||||
$(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp
|
||||
$(object.path)/ares-n64-cartridge.o: $(ares.path)/n64/cartridge/cartridge.cpp
|
||||
$(object.path)/ares-n64-controller.o: $(ares.path)/n64/controller/controller.cpp
|
||||
$(object.path)/ares-n64-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
|
||||
$(object.path)/ares-n64-vi.o: $(ares.path)/n64/vi/vi.cpp
|
||||
$(object.path)/ares-n64-ai.o: $(ares.path)/n64/ai/ai.cpp
|
||||
$(object.path)/ares-n64-pi.o: $(ares.path)/n64/pi/pi.cpp
|
||||
$(object.path)/ares-n64-ri.o: $(ares.path)/n64/ri/ri.cpp
|
||||
$(object.path)/ares-n64-si.o: $(ares.path)/n64/si/si.cpp
|
||||
$(object.path)/ares-n64-rdram.o: $(ares.path)/n64/rdram/rdram.cpp
|
||||
$(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp
|
||||
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp
|
||||
$(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp
|
||||
|
||||
flags += -msse4.2
|
||||
options += -msse4.2
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
AI ai;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto AI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("AI");
|
||||
|
||||
stream = node->append<Node::Stream>("AI");
|
||||
stream->setChannels(2);
|
||||
stream->setFrequency(44100.0);
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto AI::unload() -> void {
|
||||
node = {};
|
||||
stream = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto AI::main() -> void {
|
||||
sample();
|
||||
step(dac.period);
|
||||
}
|
||||
|
||||
auto AI::sample() -> void {
|
||||
if(io.dmaCount == 0) return stream->sample(0.0, 0.0);
|
||||
|
||||
auto data = rdram.ram.readWord(io.dmaAddress[0]);
|
||||
auto left = i16(data >> 16);
|
||||
auto right = i16(data >> 0);
|
||||
stream->sample(left / 32768.0, right / 32768.0);
|
||||
|
||||
io.dmaAddress[0] += 4;
|
||||
io.dmaLength [0] -= 4;
|
||||
if(!io.dmaLength[0]) {
|
||||
mi.raise(MI::IRQ::AI);
|
||||
if(--io.dmaCount) {
|
||||
io.dmaAddress[0] = io.dmaAddress[1];
|
||||
io.dmaLength [0] = io.dmaLength [1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto AI::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
auto AI::power() -> void {
|
||||
Thread::reset();
|
||||
io = {};
|
||||
dac = {};
|
||||
stream->setFrequency(dac.frequency);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//Audio Interface
|
||||
|
||||
struct AI : Thread, Memory::IO<AI> {
|
||||
Node::Component node;
|
||||
Node::Stream stream;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//ai.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto main() -> void;
|
||||
auto sample() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct FIFO {
|
||||
uint24 address = 0;
|
||||
} fifo[2];
|
||||
|
||||
struct IO {
|
||||
uint1 dmaEnable = 0;
|
||||
uint24 dmaAddress[2] = {};
|
||||
uint18 dmaLength[2] = {};
|
||||
uint2 dmaCount = 0;
|
||||
uint14 dacRate = 0;
|
||||
uint4 bitRate = 0;
|
||||
} io;
|
||||
|
||||
struct DAC {
|
||||
u32 frequency = 44100;
|
||||
u32 precision = 16;
|
||||
u32 period = 93'750'000 / 44'100;
|
||||
} dac;
|
||||
};
|
||||
|
||||
extern AI ai;
|
||||
@@ -0,0 +1,9 @@
|
||||
auto AI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "AI");
|
||||
}
|
||||
|
||||
auto AI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
static const vector<string> registerNames = {
|
||||
"AI_DRAM_ADDRESS",
|
||||
"AI_LENGTH",
|
||||
"AI_CONTROL",
|
||||
"AI_STATUS",
|
||||
"AI_DACRATE",
|
||||
"AI_BITRATE",
|
||||
};
|
||||
|
||||
auto AI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address != 3) {
|
||||
//AI_LENGTH (mirrored)
|
||||
data.bit(0,17) = io.dmaLength[0];
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//AI_STATUS
|
||||
data.bit( 0) = io.dmaCount > 1;
|
||||
data.bit(20) = 1;
|
||||
data.bit(24) = 1;
|
||||
data.bit(30) = io.dmaCount > 0;
|
||||
data.bit(31) = io.dmaCount > 1;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "AI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto AI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//AI_DRAM_ADDRESS
|
||||
if(io.dmaCount < 2) {
|
||||
io.dmaAddress[io.dmaCount] = data.bit(0,23) & ~7;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//AI_LENGTH
|
||||
uint18 length = data.bit(0,17) & ~7;
|
||||
if(io.dmaCount < 2 && length) {
|
||||
io.dmaLength[io.dmaCount] = length;
|
||||
io.dmaCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//AI_CONTROL
|
||||
io.dmaEnable = data.bit(0);
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//AI_STATUS
|
||||
mi.lower(MI::IRQ::AI);
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//AI_DACRATE
|
||||
auto frequency = dac.frequency;
|
||||
io.dacRate = data.bit(0,13);
|
||||
dac.frequency = max(1, 93'750'000 / 2 / (io.dacRate + 1)) * 1.037;
|
||||
dac.period = 93'750'000 / dac.frequency;
|
||||
if(frequency != dac.frequency) stream->setFrequency(dac.frequency);
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//AI_BITRATE
|
||||
io.bitRate = data.bit(0,3);
|
||||
dac.precision = io.bitRate + 1;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "AI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
auto AI::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
}
|
||||
@@ -21,12 +21,16 @@ auto Cartridge::connect() -> void {
|
||||
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
information.name = document["game/label"].string();
|
||||
information.region = document["game/region"].string();
|
||||
information.cic = document["game/board/cic"].string();
|
||||
|
||||
if(auto memory = document["game/board/memory(type=ROM,content=Program)"]) {
|
||||
rom.allocate(memory["size"].natural());
|
||||
if(auto fp = platform->open(node, "program.rom", File::Read, File::Required)) {
|
||||
rom.load(fp);
|
||||
}
|
||||
} else {
|
||||
rom.allocate(16);
|
||||
}
|
||||
|
||||
power();
|
||||
@@ -35,6 +39,7 @@ auto Cartridge::connect() -> void {
|
||||
auto Cartridge::disconnect() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
rom.reset();
|
||||
node = {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
struct Cartridge {
|
||||
Node::Peripheral node;
|
||||
Memory::Readable<uint8> rom;
|
||||
Memory::Readable rom;
|
||||
|
||||
auto manifest() const -> string { return information.manifest; }
|
||||
auto name() const -> string { return information.name; }
|
||||
auto region() const -> string { return information.region; }
|
||||
auto cic() const -> string { return information.cic; }
|
||||
|
||||
//cartridge.cpp
|
||||
auto allocate(Node::Port) -> Node::Peripheral;
|
||||
@@ -20,6 +22,8 @@ private:
|
||||
struct Information {
|
||||
string manifest;
|
||||
string name;
|
||||
string region;
|
||||
string cic;
|
||||
} information;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ struct Controller {
|
||||
Node::Peripheral node;
|
||||
|
||||
virtual ~Controller() = default;
|
||||
virtual auto read() -> uint32 { return 0; }
|
||||
};
|
||||
|
||||
#include "port.hpp"
|
||||
|
||||
@@ -1,3 +1,60 @@
|
||||
Gamepad::Gamepad(Node::Port parent) {
|
||||
node = parent->append<Node::Peripheral>("Gamepad");
|
||||
|
||||
x = node->append<Node::Axis> ("X-axis");
|
||||
y = node->append<Node::Axis> ("Y-axis");
|
||||
up = node->append<Node::Button>("Up");
|
||||
down = node->append<Node::Button>("Down");
|
||||
left = node->append<Node::Button>("Left");
|
||||
right = node->append<Node::Button>("Right");
|
||||
b = node->append<Node::Button>("B");
|
||||
a = node->append<Node::Button>("A");
|
||||
cameraUp = node->append<Node::Button>("C-Up");
|
||||
cameraDown = node->append<Node::Button>("C-Down");
|
||||
cameraLeft = node->append<Node::Button>("C-Left");
|
||||
cameraRight = node->append<Node::Button>("C-Right");
|
||||
l = node->append<Node::Button>("L");
|
||||
r = node->append<Node::Button>("R");
|
||||
z = node->append<Node::Button>("Z");
|
||||
start = node->append<Node::Button>("Start");
|
||||
}
|
||||
|
||||
auto Gamepad::read() -> uint32 {
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
platform->input(up);
|
||||
platform->input(down);
|
||||
platform->input(left);
|
||||
platform->input(right);
|
||||
platform->input(b);
|
||||
platform->input(a);
|
||||
platform->input(cameraUp);
|
||||
platform->input(cameraDown);
|
||||
platform->input(cameraLeft);
|
||||
platform->input(cameraRight);
|
||||
platform->input(l);
|
||||
platform->input(r);
|
||||
platform->input(z);
|
||||
platform->input(start);
|
||||
|
||||
uint32 data;
|
||||
data.byte(0) = -y->value() >> 8;
|
||||
data.byte(1) = +x->value() >> 8;
|
||||
data.bit(16) = cameraRight->value();
|
||||
data.bit(17) = cameraLeft->value();
|
||||
data.bit(18) = cameraDown->value();
|
||||
data.bit(19) = cameraUp->value();
|
||||
data.bit(20) = l->value();
|
||||
data.bit(21) = r->value();
|
||||
data.bit(22) = 0;
|
||||
data.bit(23) = 0;
|
||||
data.bit(24) = right->value() & !left->value();
|
||||
data.bit(25) = left->value() & !right->value();
|
||||
data.bit(26) = down->value() & !up->value();
|
||||
data.bit(27) = up->value() & !down->value();
|
||||
data.bit(28) = start->value();
|
||||
data.bit(29) = z->value();
|
||||
data.bit(30) = b->value();
|
||||
data.bit(31) = a->value();
|
||||
return data;
|
||||
}
|
||||
@@ -1,3 +1,21 @@
|
||||
struct Gamepad : Controller {
|
||||
Node::Axis x;
|
||||
Node::Axis y;
|
||||
Node::Button up;
|
||||
Node::Button down;
|
||||
Node::Button left;
|
||||
Node::Button right;
|
||||
Node::Button b;
|
||||
Node::Button a;
|
||||
Node::Button cameraUp;
|
||||
Node::Button cameraDown;
|
||||
Node::Button cameraLeft;
|
||||
Node::Button cameraRight;
|
||||
Node::Button l;
|
||||
Node::Button r;
|
||||
Node::Button z;
|
||||
Node::Button start;
|
||||
|
||||
Gamepad(Node::Port);
|
||||
auto read() -> uint32 override;
|
||||
};
|
||||
@@ -12,6 +12,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
auto ControllerPort::unload() -> void {
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "registers.hpp"
|
||||
#include "scc-tlb.cpp"
|
||||
#include "scc-registers.cpp"
|
||||
#include "fpu-registers.cpp"
|
||||
#include "memory.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 = {};
|
||||
scc.status.softReset = 1; //reset;
|
||||
fpu = {};
|
||||
LO.u64 = 0;
|
||||
HI.u64 = 0;
|
||||
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 {
|
||||
mode = min(2, self.scc.status.privilegeMode);
|
||||
if(self.scc.status.exceptionLevel) mode = Mode::Kernel;
|
||||
if(self.scc.status.errorLevel) mode = Mode::Kernel;
|
||||
|
||||
segment[0] = Segment::Mapped;
|
||||
segment[1] = Segment::Mapped;
|
||||
segment[2] = Segment::Mapped;
|
||||
segment[3] = Segment::Mapped;
|
||||
if(mode == Mode::Kernel) {
|
||||
segment[4] = Segment::Cached;
|
||||
segment[5] = Segment::Uncached;
|
||||
segment[6] = Segment::Mapped;
|
||||
segment[7] = Segment::Mapped;
|
||||
}
|
||||
if(mode == Mode::Supervisor) {
|
||||
segment[4] = Segment::Invalid;
|
||||
segment[5] = Segment::Invalid;
|
||||
segment[6] = Segment::Mapped;
|
||||
segment[7] = Segment::Invalid;
|
||||
}
|
||||
if(mode == Mode::User) {
|
||||
segment[4] = Segment::Invalid;
|
||||
segment[5] = Segment::Invalid;
|
||||
segment[6] = Segment::Invalid;
|
||||
segment[7] = Segment::Invalid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//{
|
||||
//core.cpp
|
||||
auto raiseException(uint, uint = 0) -> void;
|
||||
auto instruction() -> void;
|
||||
auto instructionEpilogue() -> bool;
|
||||
auto instructionDebug() -> void;
|
||||
auto powerR4300(bool reset) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readAddress (u32 address) -> maybe<u32>;
|
||||
auto writeAddress(u32 address) -> maybe<u32>;
|
||||
|
||||
auto readByte (u32 address) -> maybe<u32>;
|
||||
auto readHalf (u32 address) -> maybe<u32>;
|
||||
auto readWord (u32 address) -> maybe<u32>;
|
||||
auto readDouble (u32 address) -> maybe<u64>;
|
||||
|
||||
auto writeByte (u32 address, u8 data) -> bool;
|
||||
auto writeHalf (u32 address, u16 data) -> bool;
|
||||
auto writeWord (u32 address, u32 data) -> bool;
|
||||
auto writeDouble (u32 address, u64 data) -> bool;
|
||||
|
||||
//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,
|
||||
Software1 = 1,
|
||||
RCP = 2,
|
||||
Cartridge = 3,
|
||||
Reset = 4,
|
||||
ReadRDB = 5,
|
||||
WriteRDB = 6,
|
||||
Timer = 7,
|
||||
};
|
||||
|
||||
//serialization.cpp
|
||||
auto serializeR4300(serializer&) -> void;
|
||||
|
||||
struct Context {
|
||||
CPU& self;
|
||||
Context(CPU& self) : self(self) {}
|
||||
|
||||
enum Mode : uint { Kernel, Supervisor, User };
|
||||
enum Segment : uint { Invalid, Mapped, Cached, Uncached };
|
||||
uint mode;
|
||||
uint segment[8]; //512_MiB chunks
|
||||
|
||||
//core.cpp
|
||||
auto setMode() -> void;
|
||||
} context{*this};
|
||||
|
||||
#include "exception.hpp"
|
||||
#include "pipeline.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include "scc.hpp"
|
||||
#include "fpu.hpp"
|
||||
#include "recompiler.hpp"
|
||||
#include "disassembler.hpp"
|
||||
|
||||
static constexpr bool Endian = 1; //0 = little, 1 = big
|
||||
static constexpr uint FlipLE = (Endian == 0 ? 7 : 0);
|
||||
static constexpr uint FlipBE = (Endian == 1 ? 7 : 0);
|
||||
//};
|
||||
@@ -0,0 +1,584 @@
|
||||
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(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
//if(rs.i32 > ~imm) return exception.arithmeticOverflow();
|
||||
rt.u64 = i32(rs.u32 + imm);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDIU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = i32(rs.u32 + imm);
|
||||
}
|
||||
|
||||
auto CPU::instructionADDU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = i32(rs.u32 + rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionAND(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 & rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionANDI(r64& rt, cr64& rs, u16 imm) -> void {
|
||||
rt.u64 = rs.u64 & imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionBEQ(cr64& rs, cr64& rt, i16 imm) -> void {
|
||||
if(rs.u64 == rt.u64) branch.take(PC + 4 + (imm << 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::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();
|
||||
}
|
||||
|
||||
auto CPU::instructionBREAK() -> void {
|
||||
exception.breakpoint();
|
||||
}
|
||||
|
||||
auto CPU::instructionCACHE(u8 cache, u8 operation) -> void {
|
||||
//todo
|
||||
if(cache == 0) {
|
||||
//instruction
|
||||
if(operation == 0) {
|
||||
//index invalidate
|
||||
}
|
||||
if(operation == 1) {
|
||||
//index load tag
|
||||
}
|
||||
if(operation == 2) {
|
||||
//index store tag
|
||||
}
|
||||
if(operation == 4) {
|
||||
//hit invalidate
|
||||
}
|
||||
if(operation == 5) {
|
||||
//fill
|
||||
}
|
||||
if(operation == 6) {
|
||||
//hit write back
|
||||
}
|
||||
}
|
||||
if(cache == 1) {
|
||||
//data
|
||||
if(operation == 0) {
|
||||
//index write back invalidate
|
||||
}
|
||||
if(operation == 1) {
|
||||
//index load tag
|
||||
}
|
||||
if(operation == 2) {
|
||||
//index store tag
|
||||
}
|
||||
if(operation == 3) {
|
||||
//create dirty exclusive
|
||||
}
|
||||
if(operation == 4) {
|
||||
//hit invalidate
|
||||
}
|
||||
if(operation == 5) {
|
||||
//hit write back invalidate
|
||||
}
|
||||
if(operation == 6) {
|
||||
//hit write back
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionDADD(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
//if(rs.u64 > ~rt.u64) return exception.arithmeticOverflow();
|
||||
rd.u64 = rs.u64 + rt.u64;
|
||||
}
|
||||
|
||||
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(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = rs.u64 + imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionDADDU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 + rt.u64;
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
LO.u64 = rs.i64 < 0 ? +1 : -1;
|
||||
HI.u64 = rs.i64;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
} else {
|
||||
LO.u64 = rs.i32 < 0 ? +1 : -1;
|
||||
HI.u64 = rs.i32;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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(cr64& rs, cr64& rt) -> void {
|
||||
u128 result = rs.u128() * rt.u128();
|
||||
LO.u64 = result >> 0;
|
||||
HI.u64 = result >> 64;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.u64 << sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSLLV(r64& rd, cr64& rt, cr64& rs) -> 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::instructionDSRAV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.i64 >> (rs.u32 & 63);
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.u64 >> sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionDSRLV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.u64 >> (rs.u32 & 63);
|
||||
}
|
||||
|
||||
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::instructionDSUBU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 - rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionJ(u32 imm) -> void {
|
||||
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionJAL(u32 imm) -> void {
|
||||
RA.u64 = i32(PC + 8);
|
||||
branch.take((PC + 4 & 0xf000'0000) | (imm << 2));
|
||||
}
|
||||
|
||||
auto CPU::instructionJALR(r64& rd, cr64& rs) -> void {
|
||||
rd.u64 = i32(PC + 8);
|
||||
branch.take(rs.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionJR(cr64& rs) -> void {
|
||||
branch.take(rs.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionLB(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readByte(rs.u32 + imm)) rt.u64 = i8(*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(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readDouble(rs.u32 + imm)) rt.u64 = *data;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLH(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readHalf(rs.u32 + imm)) rt.u64 = i16(*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(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(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(r64& rt, u16 imm) -> void {
|
||||
rt.u64 = i32(imm << 16);
|
||||
}
|
||||
|
||||
auto CPU::instructionLW(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readWord(rs.u32 + imm)) rt.u64 = i32(*data);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionLWU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(auto data = readWord(rs.u32 + imm)) rt.u64 = u32(*data);
|
||||
}
|
||||
|
||||
auto CPU::instructionMFHI(r64& rd) -> void {
|
||||
rd.u64 = HI.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMFLO(r64& rd) -> void {
|
||||
rd.u64 = LO.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMTHI(cr64& rs) -> void {
|
||||
HI.u64 = rs.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionMTLO(cr64& rs) -> void {
|
||||
LO.u64 = rs.u64;
|
||||
}
|
||||
|
||||
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(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(r64& rd, cr64& rs, cr64& rt) -> 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(r64& rt, cr64& rs, u16 imm) -> void {
|
||||
rt.u64 = rs.u64 | imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionSB(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeByte(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSC(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
rt.u64 = writeWord(rs.u32 + imm, rt.u32);
|
||||
} else {
|
||||
rt.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSCD(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
if(scc.llbit) {
|
||||
scc.llbit = 0;
|
||||
rt.u64 = writeDouble(rs.u32 + imm, rt.u64);
|
||||
} else {
|
||||
rt.u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSD(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeDouble(rs.u32 + imm, rt.u64);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSH(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeHalf(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = i32(rt.u32 << sa);
|
||||
}
|
||||
|
||||
auto CPU::instructionSLLV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = i32(rt.u32 << (rs.u32 & 31));
|
||||
}
|
||||
|
||||
auto CPU::instructionSLT(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.i64 < rt.i64;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTI(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = rs.i64 < imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTIU(r64& rt, cr64& rs, i16 imm) -> void {
|
||||
rt.u64 = rs.u64 < imm;
|
||||
}
|
||||
|
||||
auto CPU::instructionSLTU(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 < rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionSRA(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = rt.i32 >> sa;
|
||||
}
|
||||
|
||||
auto CPU::instructionSRAV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = rt.i32 >> (rs.u32 & 31);
|
||||
}
|
||||
|
||||
auto CPU::instructionSRL(r64& rd, cr64& rt, u8 sa) -> void {
|
||||
rd.u64 = i32(rt.u32 >> sa);
|
||||
}
|
||||
|
||||
auto CPU::instructionSRLV(r64& rd, cr64& rt, cr64& rs) -> void {
|
||||
rd.u64 = i32(rt.u32 >> (rs.u32 & 31));
|
||||
}
|
||||
|
||||
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(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = i32(rs.u32 - rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionSW(cr64& rt, cr64& rs, i16 imm) -> void {
|
||||
writeWord(rs.u32 + imm, rt.u32);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionSYNC() -> void {
|
||||
//no operation; for compatibility with R4000-series code
|
||||
}
|
||||
|
||||
auto CPU::instructionSYSCALL() -> void {
|
||||
exception.systemCall();
|
||||
}
|
||||
|
||||
auto CPU::instructionTEQ(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.u64 == rt.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTEQI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 == imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGE(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.i64 >= rt.i64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 >= imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEIU(cr64& rs, i16 imm) -> void {
|
||||
if(rs.u64 >= imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTGEU(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.u64 >= rt.u64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLT(cr64& rs, cr64& rt) -> void {
|
||||
if(rs.i64 < rt.i64) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTI(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 < imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTIU(cr64& rs, i16 imm) -> void {
|
||||
if(rs.u64 < imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionTLTU(cr64& rs, cr64& rt) -> 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(cr64& rs, i16 imm) -> void {
|
||||
if(rs.i64 != imm) exception.trap();
|
||||
}
|
||||
|
||||
auto CPU::instructionXOR(r64& rd, cr64& rs, cr64& rt) -> void {
|
||||
rd.u64 = rs.u64 ^ rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::instructionXORI(r64& rt, cr64& rs, u16 imm) -> void {
|
||||
rt.u64 = rs.u64 ^ imm;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
//{
|
||||
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 {
|
||||
R0, //zero (read-only)
|
||||
AT, //assembler temporary
|
||||
V0, V1, //arithmetic values
|
||||
A0, A1, A2, A3, //subroutine parameters
|
||||
T0, T1, T2, T3, T4, T5, T6, T7, //temporary registers
|
||||
S0, S1, S2, S3, S4, S5, S6, S7, //saved registers
|
||||
T8, T9, //temporary registers
|
||||
K0, K1, //kernel registers
|
||||
GP, //global pointer
|
||||
SP, //stack pointer
|
||||
S8, //saved register
|
||||
RA, //return address
|
||||
};
|
||||
|
||||
r64 r[32];
|
||||
r64 lo;
|
||||
r64 hi;
|
||||
u64 pc; //program counter
|
||||
} core;
|
||||
|
||||
struct Branch {
|
||||
enum : u32 { Step, Take, DelaySlot, Exception, Discard };
|
||||
|
||||
auto inDelaySlot() const -> bool { return state == DelaySlot; }
|
||||
auto reset() -> void { state = Step; }
|
||||
auto take(u32 address) -> void { state = Take; pc = address; }
|
||||
auto delaySlot() -> void { state = DelaySlot; }
|
||||
auto exception() -> void { state = Exception; }
|
||||
auto discard() -> void { state = Discard; }
|
||||
|
||||
u64 pc;
|
||||
u32 state;
|
||||
} branch;
|
||||
|
||||
//cpu-instructions.cpp
|
||||
auto instructionADD(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(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(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
|
||||
@@ -0,0 +1,483 @@
|
||||
auto CPU::Disassembler::disassemble(u32 address, u32 instruction) -> string {
|
||||
this->address = address;
|
||||
this->instruction = instruction;
|
||||
|
||||
auto v = EXECUTE();
|
||||
if(!v) v.append("invalid", string{"$", hex(instruction, 8L)});
|
||||
if(!instruction) v = {"nop"};
|
||||
auto s = pad(v.takeFirst(), -8L);
|
||||
return {s, v.merge(",")};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::EXECUTE() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto ftName = [&] { return fpuRegisterName (instruction >> 16 & 31); };
|
||||
auto ftValue = [&] { return fpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto imm16i = [&] { return immediate(i16(instruction)); };
|
||||
auto imm16u = [&] { return immediate(u16(instruction), 16L); };
|
||||
auto jump = [&] { return immediate(address + 4 & 0xf000'0000 | (instruction & 0x03ff'ffff) << 2); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
auto offset = [&] { return cpuRegisterIndex(instruction >> 21 & 31, i16(instruction)); };
|
||||
|
||||
auto ALU = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), rsValue(), immediate(u16(instruction))};
|
||||
};
|
||||
|
||||
auto ADDI = [&](string_view add, string_view sub, string_view mov) -> vector<string> {
|
||||
if(!(instruction >> 21 & 31)) return {mov, rtName(), immediate(i16(instruction), 32L)};
|
||||
return {i16(instruction) >= 0 ? add : sub, rtName(), rsValue(), immediate(abs(i16(instruction)))};
|
||||
};
|
||||
|
||||
auto BRANCH1 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
auto BRANCH2 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), rtValue(), branch()};
|
||||
};
|
||||
|
||||
auto CACHE = [&](string_view name) -> vector<string> {
|
||||
auto cache = instruction >> 16 & 3;
|
||||
auto op = instruction >> 18 & 7;
|
||||
string type = "reserved";
|
||||
if(cache == 0) switch(op) {
|
||||
case 0: type = "code(IndexInvalidate)"; break;
|
||||
case 1: type = "code(IndexLoadTag)"; break;
|
||||
case 2: type = "code(IndexStoreTag)"; break;
|
||||
case 4: type = "code(HitInvalidate)"; break;
|
||||
case 5: type = "code(Fill)"; break;
|
||||
case 6: type = "code(HitWriteBack)"; break;
|
||||
}
|
||||
if(cache == 1) switch(op) {
|
||||
case 0: type = "data(IndexWriteBackInvalidate)"; break;
|
||||
case 1: type = "data(IndexLoadTag)"; break;
|
||||
case 2: type = "data(IndexStoreTag)"; break;
|
||||
case 3: type = "data(CreateDirtyExclusive)"; break;
|
||||
case 4: type = "data(HitInvalidate)"; break;
|
||||
case 5: type = "data(HitWriteBackInvalidate)"; break;
|
||||
case 6: type = "data(HitWriteBack)"; break;
|
||||
}
|
||||
return {name, type, offset()};
|
||||
};
|
||||
|
||||
auto JUMP = [&](string_view name) -> vector<string> {
|
||||
return {name, jump()};
|
||||
};
|
||||
|
||||
auto LOAD = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), offset()};
|
||||
};
|
||||
|
||||
auto STORE = [&](string_view name) -> vector<string> {
|
||||
return {name, rtValue(), offset()};
|
||||
};
|
||||
|
||||
switch(instruction >> 26) {
|
||||
case 0x00: return SPECIAL();
|
||||
case 0x01: return REGIMM();
|
||||
case 0x02: return JUMP("j");
|
||||
case 0x03: return JUMP("jal");
|
||||
case 0x04: return BRANCH2("beq");
|
||||
case 0x05: return BRANCH2("bne");
|
||||
case 0x06: return BRANCH1("blez");
|
||||
case 0x07: return BRANCH1("bgtz");
|
||||
case 0x08: return ADDI("addi", "subi", "li");
|
||||
case 0x09: return ADDI("addiu", "subiu", "liu");
|
||||
case 0x0a: return ALU("slti");
|
||||
case 0x0b: return ALU("sltiu");
|
||||
case 0x0c: return ALU("andi");
|
||||
case 0x0d: return ALU("ori");
|
||||
case 0x0e: return ALU("xori");
|
||||
case 0x0f: return {"lui", rtName(), imm16u()};
|
||||
case 0x10: return SCC();
|
||||
case 0x11: return FPU();
|
||||
case 0x12: break; //COP2
|
||||
case 0x13: break; //COP3
|
||||
case 0x14: return BRANCH2("beql");
|
||||
case 0x15: return BRANCH2("bnel");
|
||||
case 0x16: return BRANCH1("blezl");
|
||||
case 0x17: return BRANCH1("bgtzl");
|
||||
case 0x18: return ADDI("daddi", "dsubi", "dli");
|
||||
case 0x19: return ADDI("daddiu", "dsubiu", "dliu");
|
||||
case 0x1a: return LOAD("ldl");
|
||||
case 0x1b: return LOAD("ldr");
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
case 0x20: return LOAD("lb");
|
||||
case 0x21: return LOAD("lh");
|
||||
case 0x22: return LOAD("lwl");
|
||||
case 0x23: return LOAD("lw");
|
||||
case 0x24: return LOAD("lbu");
|
||||
case 0x25: return LOAD("lhu");
|
||||
case 0x26: return LOAD("lwr");
|
||||
case 0x27: return LOAD("lwu");
|
||||
case 0x28: return STORE("sb");
|
||||
case 0x29: return STORE("sh");
|
||||
case 0x2a: return STORE("swl");
|
||||
case 0x2b: return STORE("sw");
|
||||
case 0x2c: return STORE("sdl");
|
||||
case 0x2d: return STORE("sdr");
|
||||
case 0x2e: return STORE("swr");
|
||||
case 0x2f: return CACHE("cache");
|
||||
case 0x30: return LOAD("ll");
|
||||
case 0x31: return {"lwc1", ftName(), offset()};
|
||||
case 0x32: break; //LWC2
|
||||
case 0x33: break; //LWC3
|
||||
case 0x34: return LOAD("lld");
|
||||
case 0x35: return {"ldc1", ftName(), offset()};
|
||||
case 0x36: break; //LDC2
|
||||
case 0x37: return LOAD("ld");
|
||||
case 0x38: return STORE("sc");
|
||||
case 0x39: return {"swc1", ftValue(), offset()};
|
||||
case 0x3a: break; //SWC2
|
||||
case 0x3b: break; //SWC3
|
||||
case 0x3c: return STORE("scd");
|
||||
case 0x3d: return {"sdc1", ftValue(), offset()};
|
||||
case 0x3e: break; //SDC2
|
||||
case 0x3f: return STORE("sd");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::SPECIAL() -> vector<string> {
|
||||
auto shift = [&] { return string{instruction >> 6 & 31}; };
|
||||
auto rdName = [&] { return cpuRegisterName (instruction >> 11 & 31); };
|
||||
auto rdValue = [&] { return cpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
|
||||
auto ALU = [&](string_view name, string_view by) -> vector<string> {
|
||||
return {name, rdName(), rtValue(), by};
|
||||
};
|
||||
|
||||
auto JALR = [&](string_view name) -> vector<string> {
|
||||
if((instruction >> 11 & 31) == 31) return {name, rsValue()};
|
||||
return {name, rdName(), rsValue()};
|
||||
};
|
||||
|
||||
auto REG = [&](string_view name) -> vector<string> {
|
||||
return {name, rdName(), rsValue(), rtValue()};
|
||||
};
|
||||
|
||||
auto ST = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), rtValue()};
|
||||
};
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return ALU("sll", shift());
|
||||
case 0x01: break;
|
||||
case 0x02: return ALU("srl", shift());
|
||||
case 0x03: return ALU("sra", shift());
|
||||
case 0x04: return ALU("sllv", rsValue());
|
||||
case 0x05: break;
|
||||
case 0x06: return ALU("srlv", rsValue());
|
||||
case 0x07: return ALU("srav", rsValue());
|
||||
case 0x08: return {"jr", rsValue()};
|
||||
case 0x09: return JALR("jalr");
|
||||
case 0x0a: break;
|
||||
case 0x0b: break;
|
||||
case 0x0c: return {"syscall"};
|
||||
case 0x0d: return {"break"};
|
||||
case 0x0e: break;
|
||||
case 0x0f: return {"sync"};
|
||||
case 0x10: return {"mfhi", rdName(), {"hi", hint("{$", hex(self.core.hi.u64, 8L), "}")}};
|
||||
case 0x11: return {"mthi", rsValue(), "hi"};
|
||||
case 0x12: return {"mflo", rdName(), {"lo", hint("{$", hex(self.core.lo.u64, 8L), "}")}};
|
||||
case 0x13: return {"mtlo", rsValue(), "lo"};
|
||||
case 0x14: return ALU("dsllv", rsValue());
|
||||
case 0x15: break;
|
||||
case 0x16: return ALU("dsrlv", rsValue());
|
||||
case 0x17: return ALU("dsrav", rsValue());
|
||||
case 0x18: return ST("mult");
|
||||
case 0x19: return ST("multu");
|
||||
case 0x1a: return ST("div");
|
||||
case 0x1b: return ST("divu");
|
||||
case 0x1c: return ST("dmult");
|
||||
case 0x1d: return ST("dmultu");
|
||||
case 0x1e: return ST("ddiv");
|
||||
case 0x1f: return ST("ddivu");
|
||||
case 0x20: return REG("add");
|
||||
case 0x21: return REG("addu");
|
||||
case 0x22: return REG("sub");
|
||||
case 0x23: return REG("subu");
|
||||
case 0x24: return REG("and");
|
||||
case 0x25: return REG("or");
|
||||
case 0x26: return REG("xor");
|
||||
case 0x27: return REG("nor");
|
||||
case 0x28: break;
|
||||
case 0x29: break;
|
||||
case 0x2a: return REG("slt");
|
||||
case 0x2b: return REG("sltu");
|
||||
case 0x2c: return REG("dadd");
|
||||
case 0x2d: return REG("daddu");
|
||||
case 0x2e: return REG("dsub");
|
||||
case 0x2f: return REG("dsubu");
|
||||
case 0x30: return ST("tge");
|
||||
case 0x31: return ST("tgeu");
|
||||
case 0x32: return ST("tlt");
|
||||
case 0x33: return ST("tltu");
|
||||
case 0x34: return ST("teq");
|
||||
case 0x35: break;
|
||||
case 0x36: return ST("tne");
|
||||
case 0x37: break;
|
||||
case 0x38: return ALU("dsll", shift());
|
||||
case 0x39: break;
|
||||
case 0x3a: return ALU("dsrl", shift());
|
||||
case 0x3b: return ALU("dsra", shift());
|
||||
case 0x3c: return ALU("dsll32", shift());
|
||||
case 0x3d: break;
|
||||
case 0x3e: return ALU("dsrl32", shift());
|
||||
case 0x3f: return ALU("dsra32", shift());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::REGIMM() -> vector<string> {
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto imm16i = [&] { return immediate(i16(instruction)); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
auto BRANCH = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
auto TRAP = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), imm16i()};
|
||||
};
|
||||
|
||||
switch(instruction >> 16 & 0x1f) {
|
||||
case 0x00: return BRANCH("bltz");
|
||||
case 0x01: return BRANCH("bgez");
|
||||
case 0x02: return BRANCH("bltzl");
|
||||
case 0x03: return BRANCH("bgezl");
|
||||
case 0x04: break;
|
||||
case 0x05: break;
|
||||
case 0x06: break;
|
||||
case 0x07: break;
|
||||
case 0x08: return TRAP("tgei");
|
||||
case 0x09: return TRAP("tgeiu");
|
||||
case 0x0a: return TRAP("tlti");
|
||||
case 0x0b: return TRAP("tltiu");
|
||||
case 0x0c: return TRAP("teqi");
|
||||
case 0x0d: break;
|
||||
case 0x0e: return TRAP("tnei");
|
||||
case 0x0f: break;
|
||||
case 0x10: return BRANCH("bltzal");
|
||||
case 0x11: return BRANCH("bgezal");
|
||||
case 0x12: return BRANCH("bltzall");
|
||||
case 0x13: return BRANCH("bgezall");
|
||||
case 0x14: break;
|
||||
case 0x15: break;
|
||||
case 0x16: break;
|
||||
case 0x17: break;
|
||||
case 0x18: break;
|
||||
case 0x19: break;
|
||||
case 0x1a: break;
|
||||
case 0x1b: break;
|
||||
case 0x1c: break;
|
||||
case 0x1d: break;
|
||||
case 0x1e: break;
|
||||
case 0x1f: break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::SCC() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto sdName = [&] { return sccRegisterName (instruction >> 11 & 31); };
|
||||
auto sdValue = [&] { return sccRegisterValue(instruction >> 11 & 31); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
case 0x00: return {"mfc0", rtName(), sdValue()};
|
||||
case 0x01: return {"dmfc0", rtName(), sdValue()};
|
||||
case 0x02: break; //CFC0
|
||||
case 0x04: return {"mtc0", rtValue(), sdName()};
|
||||
case 0x05: return {"dmtc0", rtValue(), sdName()};
|
||||
case 0x06: break; //CTC0
|
||||
case 0x08: break; //BC0
|
||||
}
|
||||
if(!(instruction >> 25 & 1)) return {};
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x01: return {"tlbr"};
|
||||
case 0x02: return {"tlbwi"};
|
||||
case 0x06: return {"tlbwr"};
|
||||
case 0x08: return {"tlbp"};
|
||||
case 0x18: return {"eret"};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::FPU() -> vector<string> {
|
||||
auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); };
|
||||
auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); };
|
||||
auto rdName = [&] { return fpuRegisterName (instruction >> 11 & 31); };
|
||||
auto rdValue = [&] { return fpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto cdName = [&] { return ccrRegisterName (instruction >> 11 & 31); };
|
||||
auto cdValue = [&] { return ccrRegisterValue(instruction >> 11 & 31); }; //todo
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
case 0x00: return {"mfc1", rtName(), rdValue()};
|
||||
case 0x01: return {"dmfc1", rtName(), rdValue()};
|
||||
case 0x02: return {"cfc1", rtName(), cdValue()};
|
||||
case 0x04: return {"mtc1", rtValue(), rdName()};
|
||||
case 0x05: return {"dmtc1", rtValue(), rdName()};
|
||||
case 0x06: return {"ctc1", rtValue(), cdName()};
|
||||
case 0x08: switch(instruction >> 16 & 3) {
|
||||
case 0x00: return {"bc1f", branch()};
|
||||
case 0x01: return {"bc1t", branch()};
|
||||
case 0x02: return {"bc1fl", branch()};
|
||||
case 0x03: return {"bc1tl", branch()};
|
||||
}
|
||||
}
|
||||
if(!(instruction >> 25 & 1)) return {};
|
||||
|
||||
auto fdName = [&] { return fpuRegisterName (instruction >> 6 & 31); };
|
||||
auto fsValue = [&] { return fpuRegisterValue(instruction >> 11 & 31); };
|
||||
auto ftValue = [&] { return fpuRegisterValue(instruction >> 16 & 31); };
|
||||
|
||||
auto DS = [&](string_view name) -> vector<string> {
|
||||
return {name, fdName(), fsValue()};
|
||||
};
|
||||
|
||||
auto DST = [&](string_view name) -> vector<string> {
|
||||
return {name, fdName(), fsValue(), ftValue()};
|
||||
};
|
||||
|
||||
auto ST = [&](string_view name) -> vector<string> {
|
||||
return {name, fsValue(), ftValue()};
|
||||
};
|
||||
|
||||
bool s = (instruction & 1 << 21) == 0;
|
||||
bool i = (instruction & 1 << 23) != 0;
|
||||
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x00: return DST(s ? "adds" : "addd" );
|
||||
case 0x01: return DST(s ? "subs" : "subd" );
|
||||
case 0x02: return DST(s ? "muls" : "muld" );
|
||||
case 0x03: return DST(s ? "divs" : "divd" );
|
||||
case 0x04: return DS (s ? "sqrts" : "sqrtd" );
|
||||
case 0x05: return DS (s ? "abss" : "absd" );
|
||||
case 0x06: return DS (s ? "movs" : "movd" );
|
||||
case 0x07: return DS (s ? "negs" : "negd" );
|
||||
case 0x08: return DS (s ? "roundls" : "roundld");
|
||||
case 0x09: return DS (s ? "truncls" : "truncld");
|
||||
case 0x0a: return DS (s ? "ceills" : "ceilld" );
|
||||
case 0x0b: return DS (s ? "floorls" : "floorld");
|
||||
case 0x0c: return DS (s ? "roundws" : "roundwd");
|
||||
case 0x0d: return DS (s ? "truncws" : "truncwd");
|
||||
case 0x0e: return DS (s ? "ceilws" : "ceilwd" );
|
||||
case 0x0f: return DS (s ? "floorws" : "floorwd");
|
||||
case 0x20: return DS (i ? (s ? "cvtsw" : "cvtsl") : "cvtsd");
|
||||
case 0x21: return DS (i ? (s ? "cvtdw" : "cvtdl") : "cvtds");
|
||||
case 0x24: return DS (s ? "cvtws" : "cvtwd" );
|
||||
case 0x25: return DS (s ? "cvtls" : "cvtld" );
|
||||
case 0x30: 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 {};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::immediate(i64 value, uint bits) const -> string {
|
||||
if(value < 0) return {"-$", hex(-value, bits >> 2)};
|
||||
return {"$", hex(value, bits >> 2)};
|
||||
};
|
||||
|
||||
auto CPU::Disassembler::cpuRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
||||
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
||||
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::cpuRegisterValue(uint index) const -> string {
|
||||
if(index && showValues) return {cpuRegisterName(index), hint("{$", hex(self.core.r[index].u64, 8L), "}")};
|
||||
return cpuRegisterName(index);
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::cpuRegisterIndex(uint index, i16 offset) const -> string {
|
||||
string adjust;
|
||||
if(offset >= 0) adjust = {"+$", hex( offset)};
|
||||
if(offset < 0) adjust = {"-$", hex(-offset)};
|
||||
if(index && showValues) return {cpuRegisterName(index), adjust, hint("{$", hex(self.core.r[index].u64 + offset, 8L), "}")};
|
||||
return {cpuRegisterName(index), adjust};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::sccRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"Index", "Random", "EntryLo0", "EntryLo1",
|
||||
"Context", "PageMask", "Wired", "Unused7",
|
||||
"BadVAddr", "Count", "EntryHi", "Compare",
|
||||
"Status", "Cause", "EPC", "PrID",
|
||||
"Config", "LLAddr", "WatchLo", "WatchHi",
|
||||
"XContext", "Unused21", "Unused22", "Unused23",
|
||||
"Unused24", "Unused25", "ParityError", "CacheError",
|
||||
"TagLo", "TagHi", "ErrorEPC", "Unused31",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::sccRegisterValue(uint index) const -> string {
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index), 8L), "}")};
|
||||
return sccRegisterName(index);
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::fpuRegisterName(uint index) const -> string {
|
||||
static const string registers[32] = {
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||||
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
|
||||
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
|
||||
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
|
||||
};
|
||||
return registers[index];
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::fpuRegisterValue(uint index) const -> string {
|
||||
bool f32 = (instruction & 1 << 21) == 0;
|
||||
bool f64 = (instruction & 1 << 21) != 0;
|
||||
if(f32 && showValues) return {fpuRegisterName(index), hint("{", self.fpu.r[index].f32, "}")};
|
||||
if(f64 && showValues) return {fpuRegisterName(index), hint("{", self.fpu.r[index].f64, "}")};
|
||||
return fpuRegisterName(index);
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::ccrRegisterName(uint index) const -> string {
|
||||
return {"ccr", index};
|
||||
}
|
||||
|
||||
auto CPU::Disassembler::ccrRegisterValue(uint index) const -> string {
|
||||
if(showValues) return {ccrRegisterName(index), hint("{$", hex(self.getControlRegisterFPU(index)), "}")};
|
||||
return ccrRegisterName(index);
|
||||
}
|
||||
|
||||
template<typename... P>
|
||||
auto CPU::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//{
|
||||
struct Disassembler {
|
||||
CPU& self;
|
||||
Disassembler(CPU& self) : self(self) {}
|
||||
|
||||
//disassembler.cpp
|
||||
auto disassemble(u32 address, u32 instruction) -> string;
|
||||
template<typename... P> auto hint(P&&... p) const -> string;
|
||||
|
||||
bool showColors = true;
|
||||
bool showValues = true;
|
||||
|
||||
private:
|
||||
auto EXECUTE() -> vector<string>;
|
||||
auto SPECIAL() -> vector<string>;
|
||||
auto REGIMM() -> vector<string>;
|
||||
auto SCC() -> vector<string>;
|
||||
auto FPU() -> vector<string>;
|
||||
auto immediate(i64 value, uint bits = 0) const -> string;
|
||||
auto cpuRegisterName(uint index) const -> string;
|
||||
auto cpuRegisterValue(uint index) const -> string;
|
||||
auto cpuRegisterIndex(uint index, i16 offset) const -> string;
|
||||
auto sccRegisterName(uint index) const -> string;
|
||||
auto sccRegisterValue(uint index) const -> string;
|
||||
auto fpuRegisterName(uint index) const -> string;
|
||||
auto fpuRegisterValue(uint index) const -> string;
|
||||
auto ccrRegisterName(uint index) const -> string;
|
||||
auto ccrRegisterValue(uint index) const -> string;
|
||||
|
||||
u32 address;
|
||||
u32 instruction;
|
||||
} disassembler{*this};
|
||||
//};
|
||||
@@ -0,0 +1,23 @@
|
||||
//{
|
||||
struct Exception {
|
||||
CPU& self;
|
||||
auto tlbModification() { self.raiseException(1); }
|
||||
auto tlbLoad() { self.raiseException(2); }
|
||||
auto tlbStore() { self.raiseException(3); }
|
||||
auto addressLoad() { self.raiseException(4); }
|
||||
auto addressStore() { self.raiseException(5); }
|
||||
auto busInstruction() { self.raiseException(6); }
|
||||
auto busData() { self.raiseException(7); }
|
||||
auto systemCall() { self.raiseException(8); }
|
||||
auto breakpoint() { self.raiseException(9); }
|
||||
auto reservedInstruction() { self.raiseException(10); }
|
||||
auto coprocessor0() { self.raiseException(11, 0); }
|
||||
auto coprocessor1() { self.raiseException(11, 1); }
|
||||
auto coprocessor2() { self.raiseException(11, 2); }
|
||||
auto coprocessor3() { self.raiseException(11, 3); }
|
||||
auto arithmeticOverflow() { self.raiseException(12); }
|
||||
auto trap() { self.raiseException(13); }
|
||||
auto floatingPoint() { self.raiseException(15); }
|
||||
auto watchAddress() { self.raiseException(23); }
|
||||
} exception{*this};
|
||||
//};
|
||||
@@ -0,0 +1,191 @@
|
||||
#define CF fpu.csr.compare
|
||||
#define FD(type) fgr<type>(fd)
|
||||
#define FS(type) fgr<type>(fs)
|
||||
#define FT(type) fgr<type>(ft)
|
||||
|
||||
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(r64& rt, u8 rd) -> void {
|
||||
rt.u64 = i32(getControlRegisterFPU(rd));
|
||||
}
|
||||
|
||||
auto CPU::instructionCTC1(cr64& rt, u8 rd) -> void {
|
||||
setControlRegisterFPU(rd, rt.u32);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMFC1(r64& rt, u8 fs) -> void {
|
||||
rt.u64 = FS(u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMTC1(cr64& rt, u8 fs) -> void {
|
||||
FS(u64) = rt.u64;
|
||||
}
|
||||
|
||||
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::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::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::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::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::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::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::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::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::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::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::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::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::instructionLDC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
if(auto data = readDouble(rs.u32 + imm)) FT(u64) = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionLWC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
if(auto data = readWord(rs.u32 + imm)) FT(u32) = *data;
|
||||
}
|
||||
|
||||
auto CPU::instructionMFC1(r64& rt, u8 fs) -> void {
|
||||
rt.u64 = FS(i32);
|
||||
}
|
||||
|
||||
auto CPU::instructionMTC1(cr64& rt, u8 fs) -> void {
|
||||
FS(i32) = rt.u32;
|
||||
}
|
||||
|
||||
auto CPU::instructionSDC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
writeDouble(rs.u32 + imm, FT(u64));
|
||||
}
|
||||
|
||||
auto CPU::instructionSWC1(u8 ft, cr64& rs, i16 imm) -> void {
|
||||
if(!scc.status.enable.coprocessor1) return exception.coprocessor1();
|
||||
writeWord(rs.u32 + imm, FT(u32));
|
||||
}
|
||||
|
||||
#undef CF
|
||||
#undef FD
|
||||
#undef FS
|
||||
#undef FT
|
||||
@@ -0,0 +1,126 @@
|
||||
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;
|
||||
} else if(index & 1) {
|
||||
return fpu.r[index & ~1].i32h;
|
||||
} else {
|
||||
return fpu.r[index & ~1].i32;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<u32>(uint index) -> u32& {
|
||||
return (u32&)fgr<i32>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<f32>(uint index) -> f32& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].f32;
|
||||
} else if(index & 1) {
|
||||
return fpu.r[index & ~1].f32h;
|
||||
} else {
|
||||
return fpu.r[index & ~1].f32;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<i64>(uint index) -> i64& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].i64;
|
||||
} else {
|
||||
return fpu.r[index & ~1].i64;
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<u64>(uint index) -> u64& {
|
||||
return (u64&)fgr<i64>(index);
|
||||
}
|
||||
|
||||
template<> auto CPU::fgr<f64>(uint index) -> f64& {
|
||||
if(scc.status.floatingPointMode) {
|
||||
return fpu.r[index].f64;
|
||||
} else {
|
||||
return fpu.r[index & ~1].f64;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::getControlRegisterFPU(uint5 index) -> u32 {
|
||||
uint32 data;
|
||||
switch(index) {
|
||||
case 0: //coprocessor revision identifier
|
||||
data.bit(0, 7) = fpu.coprocessor.revision;
|
||||
data.bit(8,15) = fpu.coprocessor.implementation;
|
||||
break;
|
||||
case 31: //control / status register
|
||||
data.bit( 0) = fpu.csr.roundMode.bit(0);
|
||||
data.bit( 1) = fpu.csr.roundMode.bit(1);
|
||||
data.bit( 2) = fpu.csr.flag.inexact;
|
||||
data.bit( 3) = fpu.csr.flag.underflow;
|
||||
data.bit( 4) = fpu.csr.flag.overflow;
|
||||
data.bit( 5) = fpu.csr.flag.divisionByZero;
|
||||
data.bit( 6) = fpu.csr.flag.invalidOperation;
|
||||
data.bit( 7) = fpu.csr.enable.inexact;
|
||||
data.bit( 8) = fpu.csr.enable.underflow;
|
||||
data.bit( 9) = fpu.csr.enable.overflow;
|
||||
data.bit(10) = fpu.csr.enable.divisionByZero;
|
||||
data.bit(11) = fpu.csr.enable.invalidOperation;
|
||||
data.bit(12) = fpu.csr.cause.inexact;
|
||||
data.bit(13) = fpu.csr.cause.underflow;
|
||||
data.bit(14) = fpu.csr.cause.overflow;
|
||||
data.bit(15) = fpu.csr.cause.divisionByZero;
|
||||
data.bit(16) = fpu.csr.cause.invalidOperation;
|
||||
data.bit(17) = fpu.csr.cause.unimplementedOperation;
|
||||
data.bit(23) = fpu.csr.compare;
|
||||
data.bit(24) = fpu.csr.flushed;
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::setControlRegisterFPU(uint5 index, uint32 data) -> void {
|
||||
//read-only variables are defined but commented out for documentation purposes
|
||||
switch(index) {
|
||||
case 0: //coprocessor revision identifier
|
||||
//fpu.coprocessor.revision = data.bit(0, 7);
|
||||
//fpu.coprocessor.implementation = data.bit(8,15);
|
||||
break;
|
||||
case 31: {//control / status register
|
||||
uint roundModePrevious = fpu.csr.roundMode;
|
||||
fpu.csr.roundMode.bit(0) = data.bit( 0);
|
||||
fpu.csr.roundMode.bit(1) = data.bit( 1);
|
||||
fpu.csr.flag.inexact = data.bit( 2);
|
||||
fpu.csr.flag.underflow = data.bit( 3);
|
||||
fpu.csr.flag.overflow = data.bit( 4);
|
||||
fpu.csr.flag.divisionByZero = data.bit( 5);
|
||||
fpu.csr.flag.invalidOperation = data.bit( 6);
|
||||
fpu.csr.enable.inexact = data.bit( 7);
|
||||
fpu.csr.enable.underflow = data.bit( 8);
|
||||
fpu.csr.enable.overflow = data.bit( 9);
|
||||
fpu.csr.enable.divisionByZero = data.bit(10);
|
||||
fpu.csr.enable.invalidOperation = data.bit(11);
|
||||
fpu.csr.cause.inexact = data.bit(12);
|
||||
fpu.csr.cause.underflow = data.bit(13);
|
||||
fpu.csr.cause.overflow = data.bit(14);
|
||||
fpu.csr.cause.divisionByZero = data.bit(15);
|
||||
fpu.csr.cause.invalidOperation = data.bit(16);
|
||||
fpu.csr.cause.unimplementedOperation = data.bit(17);
|
||||
fpu.csr.compare = data.bit(23);
|
||||
fpu.csr.flushed = data.bit(24);
|
||||
|
||||
if(fpu.csr.roundMode != roundModePrevious) {
|
||||
switch(fpu.csr.roundMode) {
|
||||
case 0: fesetround(FE_TONEAREST); break;
|
||||
case 1: fesetround(FE_TOWARDZERO); break;
|
||||
case 2: fesetround(FE_UPWARD); break;
|
||||
case 3: fesetround(FE_DOWNWARD); break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//{
|
||||
//fpu-registers.cpp
|
||||
template<typename T> auto fgr(uint) -> T&;
|
||||
auto getControlRegisterFPU(uint5) -> u32;
|
||||
auto setControlRegisterFPU(uint5, uint32) -> void;
|
||||
|
||||
struct FPU {
|
||||
//fpu-registers.cpp
|
||||
auto setFloatingPointMode(bool) -> void;
|
||||
|
||||
r64 r[32];
|
||||
|
||||
struct Coprocessor {
|
||||
static constexpr u8 revision = 0x00;
|
||||
static constexpr u8 implementation = 0x0b;
|
||||
} coprocessor;
|
||||
|
||||
struct ControlStatus {
|
||||
uint2 roundMode = 0;
|
||||
struct Flag {
|
||||
uint1 inexact = 0;
|
||||
uint1 underflow = 0;
|
||||
uint1 overflow = 0;
|
||||
uint1 divisionByZero = 0;
|
||||
uint1 invalidOperation = 0;
|
||||
} flag;
|
||||
struct Enable {
|
||||
uint1 inexact = 0;
|
||||
uint1 underflow = 0;
|
||||
uint1 overflow = 0;
|
||||
uint1 divisionByZero = 0;
|
||||
uint1 invalidOperation = 0;
|
||||
} enable;
|
||||
struct Cause {
|
||||
uint1 inexact = 0;
|
||||
uint1 underflow = 0;
|
||||
uint1 overflow = 0;
|
||||
uint1 divisionByZero = 0;
|
||||
uint1 invalidOperation = 0;
|
||||
uint1 unimplementedOperation = 0;
|
||||
} cause;
|
||||
uint1 compare = 0;
|
||||
uint1 flushed = 0;
|
||||
} csr;
|
||||
} fpu;
|
||||
|
||||
//fpu-instructions.cpp
|
||||
auto instructionBC1(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;
|
||||
//};
|
||||
@@ -0,0 +1,89 @@
|
||||
auto CPU::readAddress(u32 address) -> maybe<u32> {
|
||||
switch(context.segment[address >> 29]) {
|
||||
case Context::Segment::Invalid:
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto lookup = tlb.load(address)) return lookup();
|
||||
tlb.exception(address);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Uncached:
|
||||
default:
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::writeAddress(u32 address) -> maybe<u32> {
|
||||
switch(context.segment[address >> 29]) {
|
||||
case Context::Segment::Invalid:
|
||||
exception.addressStore();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto lookup = tlb.store(address)) return lookup();
|
||||
tlb.exception(address);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Uncached:
|
||||
default:
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::readByte(u32 address) -> maybe<u32> {
|
||||
if(auto physical = readAddress(address)) return bus.readByte(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readHalf(u32 address) -> maybe<u32> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 1)) return exception.addressLoad(), nothing;
|
||||
}
|
||||
if(auto physical = readAddress(address)) return bus.readHalf(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readWord(u32 address) -> maybe<u32> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 3)) return exception.addressLoad(), nothing;
|
||||
}
|
||||
if(auto physical = readAddress(address)) return bus.readWord(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::readDouble(u32 address) -> maybe<u64> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 7)) return exception.addressLoad(), nothing;
|
||||
}
|
||||
if(auto physical = readAddress(address)) return bus.readDouble(physical());
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto CPU::writeByte(u32 address, u8 data) -> bool {
|
||||
if(auto physical = writeAddress(address)) return bus.writeByte(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::writeHalf(u32 address, u16 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 1)) return exception.addressStore(), false;
|
||||
}
|
||||
if(auto physical = writeAddress(address)) return bus.writeHalf(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::writeWord(u32 address, u32 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 3)) return exception.addressStore(), false;
|
||||
}
|
||||
if(auto physical = writeAddress(address)) return bus.writeWord(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::writeDouble(u32 address, u64 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(address & 7)) return exception.addressStore(), false;
|
||||
}
|
||||
if(auto physical = writeAddress(address)) return bus.writeDouble(physical(), data), true;
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//{
|
||||
struct Pipeline {
|
||||
u32 address;
|
||||
u32 instruction;
|
||||
|
||||
struct InstructionCache {
|
||||
} ic;
|
||||
|
||||
struct RegisterFile {
|
||||
} rf;
|
||||
|
||||
struct Execution {
|
||||
} ex;
|
||||
|
||||
struct DataCache {
|
||||
} dc;
|
||||
|
||||
struct WriteBack {
|
||||
} wb;
|
||||
} pipeline;
|
||||
//};
|
||||
@@ -0,0 +1,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};
|
||||
//};
|
||||
@@ -0,0 +1,20 @@
|
||||
#if !defined(CPU_REGISTERS_HPP)
|
||||
#define CPU_REGISTERS_HPP
|
||||
|
||||
//CPU
|
||||
#define GPR core.r
|
||||
#define LO core.lo
|
||||
#define HI core.hi
|
||||
#define PC core.pc
|
||||
|
||||
#define RA GPR[Core::Register::RA]
|
||||
#else
|
||||
#undef CPU_REGISTERS_HPP
|
||||
|
||||
//CPU
|
||||
#undef GPR
|
||||
#undef LO
|
||||
#undef HI
|
||||
#undef PC
|
||||
#undef RA
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
auto CPU::instructionDMFC0(r64& rt, u8 rd) -> void {
|
||||
rt.u64 = getControlRegister(rd);
|
||||
}
|
||||
|
||||
auto CPU::instructionDMTC0(cr64& rt, u8 rd) -> void {
|
||||
setControlRegister(rd, rt.u64);
|
||||
}
|
||||
|
||||
auto CPU::instructionERET() -> void {
|
||||
branch.exception();
|
||||
if(scc.status.errorLevel) {
|
||||
PC = scc.epcError;
|
||||
scc.status.errorLevel = 0;
|
||||
} else {
|
||||
PC = scc.epc;
|
||||
scc.status.exceptionLevel = 0;
|
||||
}
|
||||
scc.llbit = 0;
|
||||
context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::instructionMFC0(r64& rt, u8 rd) -> void {
|
||||
rt.u64 = i32(getControlRegister(rd));
|
||||
}
|
||||
|
||||
auto CPU::instructionMTC0(cr64& rt, u8 rd) -> void {
|
||||
setControlRegister(rd, i32(rt.u32));
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBP() -> void {
|
||||
scc.index.tlbEntry = 0; //technically undefined
|
||||
scc.index.probeFailure = 1;
|
||||
for(uint index : range(TLB::Entries)) {
|
||||
auto& entry = tlb.entry[index];
|
||||
auto mask = ~entry.pageMask & ~0x1fff;
|
||||
if((entry.virtualAddress & mask) != (scc.tlb.virtualAddress & mask)) continue;
|
||||
if(!entry.global[0] || !entry.global[1]) {
|
||||
if(entry.addressSpaceID != scc.tlb.addressSpaceID) continue;
|
||||
}
|
||||
scc.index.tlbEntry = index;
|
||||
scc.index.probeFailure = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBR() -> void {
|
||||
if(scc.index.tlbEntry >= TLB::Entries) return;
|
||||
scc.tlb = tlb.entry[scc.index.tlbEntry];
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBWI() -> void {
|
||||
if(scc.index.tlbEntry >= TLB::Entries) return;
|
||||
tlb.entry[scc.index.tlbEntry] = scc.tlb;
|
||||
}
|
||||
|
||||
auto CPU::instructionTLBWR() -> void {
|
||||
if(scc.random.index >= TLB::Entries) return;
|
||||
tlb.entry[scc.index.tlbEntry] = scc.tlb;
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
auto CPU::getControlRegister(uint5 index) -> u64 {
|
||||
uint64 data;
|
||||
switch(index) {
|
||||
case 0: //index
|
||||
data.bit( 0, 5) = scc.index.tlbEntry;
|
||||
data.bit(31) = scc.index.probeFailure;
|
||||
break;
|
||||
case 1: //random
|
||||
data.bit(0,4) = scc.random.index;
|
||||
data.bit(5) = scc.random.unused;
|
||||
break;
|
||||
case 2: //entrylo0
|
||||
data.bit(0) = scc.tlb.global[0];
|
||||
data.bit(1) = scc.tlb.valid[0];
|
||||
data.bit(2) = scc.tlb.dirty[0];
|
||||
data.bit(3, 5) = scc.tlb.cacheAlgorithm[0];
|
||||
data.bit(6,29) = scc.tlb.physicalAddress[0].bit(12,35);
|
||||
break;
|
||||
case 3: //entrylo1
|
||||
data.bit(0) = scc.tlb.global[1];
|
||||
data.bit(1) = scc.tlb.valid[1];
|
||||
data.bit(2) = scc.tlb.dirty[1];
|
||||
data.bit(3, 5) = scc.tlb.cacheAlgorithm[1];
|
||||
data.bit(6,29) = scc.tlb.physicalAddress[1].bit(12,35);
|
||||
break;
|
||||
case 4: //context
|
||||
data.bit( 4,22) = scc.context.badVirtualAddress;
|
||||
data.bit(23,63) = scc.context.pageTableEntryBase;
|
||||
break;
|
||||
case 5: //pagemask
|
||||
data.bit(13,24) = scc.tlb.pageMask.bit(13,24);
|
||||
break;
|
||||
case 6: //wired
|
||||
data.bit(0,4) = scc.wired.index;
|
||||
data.bit(5) = scc.wired.unused;
|
||||
break;
|
||||
case 8: //badvaddr
|
||||
data = scc.badVirtualAddress;
|
||||
break;
|
||||
case 9: //count
|
||||
data.bit(0,31) = scc.count;
|
||||
break;
|
||||
case 10: //entryhi
|
||||
data.bit( 0, 7) = scc.tlb.addressSpaceID;
|
||||
data.bit(13,39) = scc.tlb.virtualAddress.bit(13,31);
|
||||
data.bit(40,61) = scc.tlb.unused;
|
||||
data.bit(62,63) = scc.tlb.region;
|
||||
break;
|
||||
case 11: //compare
|
||||
data.bit(0,31) = scc.compare;
|
||||
break;
|
||||
case 12: //status
|
||||
data.bit( 0) = scc.status.interruptEnable;
|
||||
data.bit( 1) = scc.status.exceptionLevel;
|
||||
data.bit( 2) = scc.status.errorLevel;
|
||||
data.bit( 3, 4) = scc.status.privilegeMode;
|
||||
data.bit( 5) = scc.status.userExtendedAddressing;
|
||||
data.bit( 6) = scc.status.supervisorExtendedAddressing;
|
||||
data.bit( 7) = scc.status.kernelExtendedAddressing;
|
||||
data.bit( 8,15) = scc.status.interruptMask;
|
||||
data.bit(16) = scc.status.de;
|
||||
data.bit(17) = scc.status.ce;
|
||||
data.bit(18) = scc.status.condition;
|
||||
data.bit(20) = scc.status.softReset;
|
||||
data.bit(21) = scc.status.tlbShutdown;
|
||||
data.bit(22) = scc.status.vectorLocation;
|
||||
data.bit(24) = scc.status.instructionTracing;
|
||||
data.bit(25) = scc.status.reverseEndian;
|
||||
data.bit(26) = scc.status.floatingPointMode;
|
||||
data.bit(27) = scc.status.lowPowerMode;
|
||||
data.bit(28) = scc.status.enable.coprocessor0;
|
||||
data.bit(29) = scc.status.enable.coprocessor1;
|
||||
data.bit(30) = scc.status.enable.coprocessor2;
|
||||
data.bit(31) = scc.status.enable.coprocessor3;
|
||||
context.setMode();
|
||||
break;
|
||||
case 13: //cause
|
||||
data.bit( 2, 6) = scc.cause.exceptionCode;
|
||||
data.bit( 8,15) = scc.cause.interruptPending;
|
||||
data.bit(28,29) = scc.cause.coprocessorError;
|
||||
data.bit(31) = scc.cause.branchDelay;
|
||||
break;
|
||||
case 14: //exception program counter
|
||||
data = scc.epc;
|
||||
break;
|
||||
case 15: //coprocessor revision identifier
|
||||
data.bit(0, 7) = scc.coprocessor.revision;
|
||||
data.bit(8,15) = scc.coprocessor.implementation;
|
||||
break;
|
||||
case 16: //configuration
|
||||
data.bit( 0, 1) = scc.configuration.coherencyAlgorithmKSEG0;
|
||||
data.bit( 2, 3) = scc.configuration.cu;
|
||||
data.bit(15) = scc.configuration.bigEndian;
|
||||
data.bit(24,27) = scc.configuration.sysadWritebackPattern;
|
||||
data.bit(28,30) = scc.configuration.systemClockRatio;
|
||||
break;
|
||||
case 17: //load linked address
|
||||
data = scc.ll;
|
||||
break;
|
||||
case 18: //watchlo
|
||||
data.bit(0) = scc.watchLo.trapOnWrite;
|
||||
data.bit(1) = scc.watchLo.trapOnRead;
|
||||
data.bit(3,31) = scc.watchLo.physicalAddress.bit(3,31);
|
||||
break;
|
||||
case 19: //watchhi
|
||||
data.bit(0,3) = scc.watchHi.physicalAddressExtended;
|
||||
break;
|
||||
case 20: //xcontext
|
||||
data.bit( 4,30) = scc.xcontext.badVirtualAddress;
|
||||
data.bit(31,32) = scc.xcontext.region;
|
||||
data.bit(33,63) = scc.xcontext.pageTableEntryBase;
|
||||
break;
|
||||
case 26: //parity error
|
||||
data.bit(0,7) = scc.parityError.diagnostic;
|
||||
break;
|
||||
case 28: //taglo
|
||||
data.bit(6, 7) = scc.tagLo.primaryCacheState;
|
||||
data.bit(8,27) = scc.tagLo.physicalAddress.bit(12,31);
|
||||
break;
|
||||
case 31: //error exception program counter
|
||||
data = scc.epcError;
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::setControlRegister(uint5 index, uint64 data) -> void {
|
||||
//read-only variables are defined but commented out for documentation purposes
|
||||
switch(index) {
|
||||
case 0: //index
|
||||
scc.index.tlbEntry = data.bit( 0,5);
|
||||
scc.index.probeFailure = data.bit(31);
|
||||
break;
|
||||
case 1: //random
|
||||
//scc.random.index = data.bit(0,4);
|
||||
scc.random.unused = data.bit(5);
|
||||
break;
|
||||
case 2: //entrylo0
|
||||
scc.tlb.global[0] = data.bit(0);
|
||||
scc.tlb.valid[0] = data.bit(1);
|
||||
scc.tlb.dirty[0] = data.bit(2);
|
||||
scc.tlb.cacheAlgorithm[0] = data.bit(3, 5);
|
||||
scc.tlb.physicalAddress[0].bit(12,35) = data.bit(6,29);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 3: //entrylo1
|
||||
scc.tlb.global[1] = data.bit(0);
|
||||
scc.tlb.valid[1] = data.bit(1);
|
||||
scc.tlb.dirty[1] = data.bit(2);
|
||||
scc.tlb.cacheAlgorithm[1] = data.bit(3, 5);
|
||||
scc.tlb.physicalAddress[1].bit(12,35) = data.bit(6,29);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 4: //context
|
||||
scc.context.badVirtualAddress = data.bit( 4,22);
|
||||
scc.context.pageTableEntryBase = data.bit(23,63);
|
||||
break;
|
||||
case 5: //pagemask
|
||||
scc.tlb.pageMask.bit(13,24) = data.bit(13,24);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 6: //wired
|
||||
scc.wired.index = data.bit(0,4);
|
||||
scc.wired.unused = data.bit(5);
|
||||
scc.random.index = 31;
|
||||
break;
|
||||
case 8: //badvaddr
|
||||
//scc.badVirtualAddress = data; //read-only
|
||||
break;
|
||||
case 9: //count
|
||||
scc.count = data.bit(0,31);
|
||||
break;
|
||||
case 10: //entryhi
|
||||
scc.tlb.addressSpaceID = data.bit( 0, 7);
|
||||
scc.tlb.virtualAddress.bit(13,31) = data.bit(13,39);
|
||||
scc.tlb.unused = data.bit(40,61);
|
||||
scc.tlb.region = data.bit(62,63);
|
||||
scc.tlb.synchronize();
|
||||
break;
|
||||
case 11: //compare
|
||||
scc.compare = data.bit(0,31);
|
||||
scc.cause.interruptPending.bit(Interrupt::Timer) = 0;
|
||||
break;
|
||||
case 12: {//status
|
||||
bool floatingPointMode = scc.status.floatingPointMode;
|
||||
scc.status.interruptEnable = data.bit( 0);
|
||||
scc.status.exceptionLevel = data.bit( 1);
|
||||
scc.status.errorLevel = data.bit( 2);
|
||||
scc.status.privilegeMode = data.bit( 3, 4);
|
||||
scc.status.userExtendedAddressing = data.bit( 5);
|
||||
scc.status.supervisorExtendedAddressing = data.bit( 6);
|
||||
scc.status.kernelExtendedAddressing = data.bit( 7);
|
||||
scc.status.interruptMask = data.bit( 8,15);
|
||||
scc.status.de = data.bit(16);
|
||||
scc.status.ce = data.bit(17);
|
||||
scc.status.condition = data.bit(18);
|
||||
scc.status.softReset = data.bit(20);
|
||||
//scc.status.tlbShutdown = data.bit(21); //read-only
|
||||
scc.status.vectorLocation = data.bit(22);
|
||||
scc.status.instructionTracing = data.bit(24);
|
||||
scc.status.reverseEndian = data.bit(25);
|
||||
scc.status.floatingPointMode = data.bit(26);
|
||||
scc.status.lowPowerMode = data.bit(27);
|
||||
scc.status.enable.coprocessor0 = data.bit(28);
|
||||
scc.status.enable.coprocessor1 = data.bit(29);
|
||||
scc.status.enable.coprocessor2 = data.bit(30);
|
||||
scc.status.enable.coprocessor3 = data.bit(31);
|
||||
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);
|
||||
break;
|
||||
case 14: //exception program counter
|
||||
scc.epc = data;
|
||||
break;
|
||||
case 15: //coprocessor revision identifier
|
||||
//scc.coprocessor.revision = data.bit(0, 7); //read-only
|
||||
//scc.coprocessor.implementation = data.bit(8,15); //read-only
|
||||
break;
|
||||
case 16: //configuration
|
||||
scc.configuration.coherencyAlgorithmKSEG0 = data.bit( 0, 1);
|
||||
scc.configuration.cu = data.bit( 2, 3);
|
||||
scc.configuration.bigEndian = data.bit(15);
|
||||
scc.configuration.sysadWritebackPattern = data.bit(24,27);
|
||||
//scc.configuration.systemClockRatio = data.bit(28,30); //read-only
|
||||
break;
|
||||
case 17: //load linked address
|
||||
scc.ll = data;
|
||||
break;
|
||||
case 18: //watchlo
|
||||
scc.watchLo.trapOnWrite = data.bit(0);
|
||||
scc.watchLo.trapOnRead = data.bit(1);
|
||||
scc.watchLo.physicalAddress.bit(3,31) = data.bit(3,31);
|
||||
break;
|
||||
case 19: //watchhi
|
||||
scc.watchHi.physicalAddressExtended = data.bit(0,3);
|
||||
break;
|
||||
case 20: //xcontext
|
||||
scc.xcontext.badVirtualAddress = data.bit( 4,30);
|
||||
scc.xcontext.region = data.bit(31,32);
|
||||
scc.xcontext.pageTableEntryBase = data.bit(33,63);
|
||||
break;
|
||||
case 26: //parity error
|
||||
scc.parityError.diagnostic = data.bit(0,7);
|
||||
break;
|
||||
case 28: //taglo
|
||||
scc.tagLo.primaryCacheState = data.bit(6, 7);
|
||||
scc.tagLo.physicalAddress.bit(12,31) = data.bit(8,27);
|
||||
break;
|
||||
case 31: //error exception program counter
|
||||
scc.epcError = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
auto CPU::TLB::load(u32 address) -> maybe<u32> {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals) continue;
|
||||
if((address & entry.addressMaskHi) != entry.addressCompare) continue;
|
||||
bool lo = address & entry.addressSelect;
|
||||
if(!entry.valid[lo]) return self.exception.tlbLoad(), nothing;
|
||||
return physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
|
||||
}
|
||||
return self.exception.tlbLoad(), nothing;
|
||||
}
|
||||
|
||||
auto CPU::TLB::store(u32 address) -> maybe<u32> {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals) continue;
|
||||
if((address & entry.addressMaskHi) != entry.addressCompare) continue;
|
||||
bool lo = address & entry.addressSelect;
|
||||
if(!entry.valid[lo]) return self.exception.tlbStore(), nothing;
|
||||
if(!entry.dirty[lo]) return self.exception.tlbModification(), nothing;
|
||||
return physicalAddress = entry.physicalAddress[lo] + (address & entry.addressMaskLo);
|
||||
}
|
||||
return self.exception.tlbStore(), nothing;
|
||||
}
|
||||
|
||||
auto CPU::TLB::exception(u32 address) -> void {
|
||||
self.scc.badVirtualAddress = address;
|
||||
self.scc.tlb.addressSpaceID = 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;
|
||||
}
|
||||
|
||||
auto CPU::TLB::Entry::synchronize() -> void {
|
||||
globals = global[0] && global[1];
|
||||
addressMaskHi = ~(pageMask | 0x1fff);
|
||||
addressMaskLo = (pageMask | 0x1fff) >> 1;
|
||||
addressSelect = addressMaskLo + 1;
|
||||
addressCompare = virtualAddress & addressMaskHi;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//{
|
||||
//scc-registers.cpp
|
||||
auto getControlRegister(uint5) -> u64;
|
||||
auto setControlRegister(uint5, uint64) -> void;
|
||||
|
||||
//Translation Lookaside Buffer
|
||||
struct TLB {
|
||||
CPU& self;
|
||||
TLB(CPU& self) : self(self) {}
|
||||
static constexpr uint Entries = 32;
|
||||
|
||||
//tlb.cpp
|
||||
auto load(u32 address) -> maybe<u32>;
|
||||
auto store(u32 address) -> maybe<u32>;
|
||||
auto exception(u32 address) -> void;
|
||||
|
||||
struct Entry {
|
||||
//scc-tlb.cpp
|
||||
auto synchronize() -> void;
|
||||
|
||||
uint1 global[2] = {};
|
||||
uint1 valid[2] = {};
|
||||
uint1 dirty[2] = {};
|
||||
uint3 cacheAlgorithm[2] = {2, 2};
|
||||
uint32 physicalAddress[2] = {};
|
||||
uint32 pageMask = 0;
|
||||
uint32 virtualAddress = 0;
|
||||
uint8 addressSpaceID = 0;
|
||||
//unimplemented:
|
||||
uint22 unused = 0;
|
||||
uint2 region = 0;
|
||||
//internal:
|
||||
uint1 globals = 0;
|
||||
uint32 addressMaskHi = 0;
|
||||
uint32 addressMaskLo = 0;
|
||||
uint32 addressSelect = 0;
|
||||
uint32 addressCompare = 0;
|
||||
} entry[TLB::Entries];
|
||||
|
||||
u32 physicalAddress = 0;
|
||||
} tlb{*this};
|
||||
|
||||
//System Control Coprocessor
|
||||
struct SCC {
|
||||
//0
|
||||
struct Index {
|
||||
uint6 tlbEntry = 0;
|
||||
uint1 probeFailure = 0;
|
||||
} index;
|
||||
|
||||
//1
|
||||
struct Random {
|
||||
uint5 index = 31;
|
||||
uint1 unused = 0;
|
||||
} random;
|
||||
|
||||
//2: EntryLo0
|
||||
//3: EntryLo1
|
||||
//5: PageMask
|
||||
//10: EntryHi
|
||||
TLB::Entry tlb;
|
||||
|
||||
//4
|
||||
struct Context {
|
||||
uint19 badVirtualAddress = 0;
|
||||
uint41 pageTableEntryBase = 0;
|
||||
} context;
|
||||
|
||||
//6
|
||||
struct Wired {
|
||||
uint5 index = 0;
|
||||
uint1 unused = 0;
|
||||
} wired;
|
||||
|
||||
//8
|
||||
u64 badVirtualAddress = 0;
|
||||
|
||||
//9
|
||||
u32 count = 0;
|
||||
|
||||
//11
|
||||
u32 compare = 0;
|
||||
|
||||
//12
|
||||
struct Status {
|
||||
uint1 interruptEnable = 0;
|
||||
uint1 exceptionLevel = 0;
|
||||
uint1 errorLevel = 1;
|
||||
uint2 privilegeMode = 0;
|
||||
uint1 userExtendedAddressing = 0;
|
||||
uint1 supervisorExtendedAddressing = 0;
|
||||
uint1 kernelExtendedAddressing = 0;
|
||||
uint8 interruptMask = 0xff;
|
||||
uint1 de = 0; //unused
|
||||
uint1 ce = 0; //unused
|
||||
uint1 condition = 0;
|
||||
uint1 softReset = 0;
|
||||
uint1 tlbShutdown = 0;
|
||||
uint1 vectorLocation = 1;
|
||||
uint1 instructionTracing = 0;
|
||||
uint1 reverseEndian = 0;
|
||||
uint1 floatingPointMode = 1;
|
||||
uint1 lowPowerMode = 0;
|
||||
struct Enable {
|
||||
uint1 coprocessor0 = 1;
|
||||
uint1 coprocessor1 = 1;
|
||||
uint1 coprocessor2 = 0;
|
||||
uint1 coprocessor3 = 0;
|
||||
} enable;
|
||||
} status;
|
||||
|
||||
//13
|
||||
struct Cause {
|
||||
uint5 exceptionCode = 0;
|
||||
uint8 interruptPending = 0x00;
|
||||
uint2 coprocessorError = 0;
|
||||
uint1 branchDelay = 0;
|
||||
} cause;
|
||||
|
||||
//14: Exception Program Counter
|
||||
u64 epc;
|
||||
|
||||
//15: Coprocessor Revision Identifier
|
||||
struct Coprocessor {
|
||||
static constexpr u8 revision = 0x00;
|
||||
static constexpr u8 implementation = 0x0b;
|
||||
} coprocessor;
|
||||
|
||||
//16
|
||||
struct Configuration {
|
||||
uint2 coherencyAlgorithmKSEG0 = 0;
|
||||
uint2 cu = 0; //reserved
|
||||
uint1 bigEndian = 1;
|
||||
uint2 sysadWritebackPattern = 0;
|
||||
uint2 systemClockRatio = 6;
|
||||
} configuration;
|
||||
|
||||
//17: Load Linked Address
|
||||
u64 ll;
|
||||
uint1 llbit;
|
||||
|
||||
//18
|
||||
struct WatchLo {
|
||||
uint1 trapOnWrite = 0;
|
||||
uint1 trapOnRead = 0;
|
||||
uint32 physicalAddress = 0;
|
||||
} watchLo;
|
||||
|
||||
//19
|
||||
struct WatchHi {
|
||||
uint4 physicalAddressExtended = 0; //unused; for R4000 compatibility only
|
||||
} watchHi;
|
||||
|
||||
//20
|
||||
struct XContext {
|
||||
uint27 badVirtualAddress = 0;
|
||||
uint2 region = 0;
|
||||
uint31 pageTableEntryBase = 0;
|
||||
} xcontext;
|
||||
|
||||
//26
|
||||
struct ParityError {
|
||||
uint8 diagnostic = 0; //unused; for R4000 compatibility only
|
||||
} parityError;
|
||||
|
||||
//28
|
||||
struct TagLo {
|
||||
uint2 primaryCacheState = 0;
|
||||
uint32 physicalAddress = 0;
|
||||
} tagLo;
|
||||
|
||||
//31: Error Exception Program Counter
|
||||
u64 epcError;
|
||||
} scc;
|
||||
|
||||
//scc-instructions.cpp
|
||||
auto instructionDMFC0(r64& rt, u8 rd) -> void;
|
||||
auto instructionDMTC0(cr64& rt, u8 rd) -> void;
|
||||
auto instructionERET() -> void;
|
||||
auto instructionMFC0(r64& rt, u8 rd) -> void;
|
||||
auto instructionMTC0(cr64& rt, u8 rd) -> void;
|
||||
auto instructionTLBP() -> void;
|
||||
auto instructionTLBR() -> void;
|
||||
auto instructionTLBWI() -> void;
|
||||
auto instructionTLBWR() -> void;
|
||||
//};
|
||||
@@ -0,0 +1,2 @@
|
||||
auto CPU::serializeR4300(serializer& s) -> void {
|
||||
}
|
||||
+21
-6
@@ -3,28 +3,43 @@
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
CPU cpu;
|
||||
#include "core/core.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CPU::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("CPU");
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto CPU::unload() -> void {
|
||||
node = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
step(20);
|
||||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
vi.clock -= clocks;
|
||||
ai.clock -= clocks;
|
||||
rsp.clock -= clocks;
|
||||
rdp.clock -= clocks;
|
||||
while( vi.clock < 0) vi.main();
|
||||
while( ai.clock < 0) ai.main();
|
||||
while(rsp.clock < 0) rsp.main();
|
||||
while(rdp.clock < 0) rdp.main();
|
||||
|
||||
if(scc.count < scc.compare && scc.count + clocks >= scc.compare) {
|
||||
scc.cause.interruptPending.bit(Interrupt::Timer) = 1;
|
||||
}
|
||||
scc.count += clocks;
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
VR4300::power();
|
||||
Thread::create(93'750'000, {&CPU::main, this});
|
||||
auto CPU::power(bool reset) -> void {
|
||||
Thread::reset();
|
||||
powerR4300(reset);
|
||||
}
|
||||
|
||||
}
|
||||
+18
-2
@@ -1,8 +1,22 @@
|
||||
//NEC VR4300
|
||||
|
||||
struct CPU : VR4300, Thread {
|
||||
struct CPU : Thread {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto instruction() -> void;
|
||||
auto exception(string_view) -> void;
|
||||
auto interrupt(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Instruction instruction;
|
||||
Node::Notification exception;
|
||||
Node::Notification interrupt;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//cpu.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
@@ -10,10 +24,12 @@ struct CPU : VR4300, Thread {
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
#include "core/core.hpp"
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
@@ -0,0 +1,31 @@
|
||||
auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Instruction>("Instruction", "CPU");
|
||||
tracer.instruction->setAddressBits(32);
|
||||
|
||||
tracer.exception = parent->append<Node::Notification>("Exception", "CPU");
|
||||
tracer.interrupt = parent->append<Node::Notification>("Interrupt", "CPU");
|
||||
}
|
||||
|
||||
auto CPU::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled()) {
|
||||
u32 address = cpu.pipeline.address;
|
||||
u32 instruction = cpu.pipeline.instruction;
|
||||
if(tracer.instruction->address(address)) {
|
||||
cpu.disassembler.showColors = 0;
|
||||
tracer.instruction->notify(cpu.disassembler.disassemble(address, instruction), {});
|
||||
cpu.disassembler.showColors = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Debugger::exception(string_view type) -> void {
|
||||
if(tracer.exception->enabled()) {
|
||||
tracer.exception->notify(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Debugger::interrupt(string_view type) -> void {
|
||||
if(tracer.interrupt->enabled()) {
|
||||
tracer.interrupt->notify(type);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
auto CPU::serialize(serializer& s) -> void {
|
||||
VR4300::serialize(s);
|
||||
Thread::serialize(s);
|
||||
serializeR4300(s);
|
||||
}
|
||||
@@ -0,0 +1,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 {
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#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;
|
||||
decode(0, readByte, address);
|
||||
}
|
||||
|
||||
inline auto Bus::readHalf(u32 address) -> u16 {
|
||||
address &= 0x1fff'fffe;
|
||||
decode(0, readHalf, address);
|
||||
}
|
||||
|
||||
inline auto Bus::readWord(u32 address) -> u32 {
|
||||
address &= 0x1fff'fffc;
|
||||
decode(0, readWord, address);
|
||||
}
|
||||
|
||||
inline auto Bus::readDouble(u32 address) -> u64 {
|
||||
address &= 0x1fff'fff8;
|
||||
decode(0, readDouble, address);
|
||||
}
|
||||
|
||||
#undef unmapped
|
||||
#define unmapped
|
||||
|
||||
inline auto Bus::writeByte(u32 address, u8 data) -> void {
|
||||
address &= 0x1fff'ffff;
|
||||
cpu.recompiler.invalidate(address);
|
||||
decode(1, writeByte, address, data);
|
||||
}
|
||||
|
||||
inline auto Bus::writeHalf(u32 address, u16 data) -> void {
|
||||
address &= 0x1fff'fffe;
|
||||
cpu.recompiler.invalidate(address);
|
||||
decode(1, writeHalf, address, data);
|
||||
}
|
||||
|
||||
inline auto Bus::writeWord(u32 address, u32 data) -> void {
|
||||
address &= 0x1fff'fffc;
|
||||
cpu.recompiler.invalidate(address);
|
||||
decode(1, writeWord, address, data);
|
||||
}
|
||||
|
||||
inline auto Bus::writeDouble(u32 address, u64 data) -> void {
|
||||
address &= 0x1fff'fff8;
|
||||
cpu.recompiler.invalidate(address + 0);
|
||||
cpu.recompiler.invalidate(address + 4);
|
||||
decode(2, writeDouble, address, data);
|
||||
}
|
||||
|
||||
#undef unmapped
|
||||
#undef decode
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
Bus bus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
namespace Memory {
|
||||
|
||||
struct Readable {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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(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 { *(u8*)&data[address & maskByte] = value; }
|
||||
auto writeHalf(u32 address, u16 value) -> void { *(u16*)&data[address & maskHalf] = bswap16(value); }
|
||||
auto writeWord(u32 address, u32 value) -> void { *(u32*)&data[address & maskWord] = bswap32(value); }
|
||||
auto writeDouble(u32 address, u64 value) -> void { *(u64*)&data[address & maskDouble] = bswap64(value); }
|
||||
auto writeQuad(u32 address, u128 value) -> void { *(u128*)&data[address & maskQuad] = bswap128(value); }
|
||||
|
||||
//N64 RSP allows unaligned memory accesses in certain cases
|
||||
auto readHalfUnaligned(u32 address) -> u16 { return bswap16(*(u16*)&data[address & maskByte]); }
|
||||
auto readWordUnaligned(u32 address) -> u32 { return bswap32(*(u32*)&data[address & maskByte]); }
|
||||
auto readDoubleUnaligned(u32 address) -> u64 { return bswap64(*(u64*)&data[address & maskByte]); }
|
||||
auto readQuadUnaligned(u32 address) -> u128 { return bswap128(*(u128*)&data[address & maskByte]); }
|
||||
|
||||
auto writeHalfUnaligned(u32 address, u16 value) -> void { *(u16*)&data[address & maskByte] = bswap16(value); }
|
||||
auto writeWordUnaligned(u32 address, u32 value) -> void { *(u32*)&data[address & maskByte] = bswap32(value); }
|
||||
auto writeDoubleUnaligned(u32 address, u64 value) -> void { *(u64*)&data[address & maskByte] = bswap64(value); }
|
||||
auto writeQuadUnaligned(u32 address, u128 value) -> void { *(u128*)&data[address & maskByte] = bswap128(value); }
|
||||
|
||||
//private:
|
||||
u8* data = nullptr;
|
||||
u32 size = 0;
|
||||
u32 maskByte = 0;
|
||||
u32 maskHalf = 0;
|
||||
u32 maskWord = 0;
|
||||
u32 maskDouble = 0;
|
||||
u32 maskQuad = 0;
|
||||
};
|
||||
|
||||
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;
|
||||
auto readDouble(u32 address) -> u64;
|
||||
auto writeByte(u32 address, u8 data) -> void;
|
||||
auto writeHalf(u32 address, u16 data) -> void;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto writeDouble(u32 address, u64 data) -> void;
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
@@ -0,0 +1,16 @@
|
||||
auto MI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.interrupt = parent->append<Node::Notification>("Interrupt", "RCP");
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "MI");
|
||||
}
|
||||
|
||||
auto MI::Debugger::interrupt(string_view type) -> void {
|
||||
if(tracer.interrupt->enabled()) {
|
||||
tracer.interrupt->notify(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto MI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
static const vector<string> registerNames = {
|
||||
"MI_INIT_MODE",
|
||||
"MI_VERSION",
|
||||
"MI_INTR",
|
||||
"MI_INTR_MASK",
|
||||
};
|
||||
|
||||
auto MI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//MI_INIT_MODE
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//MI_VERSION
|
||||
data.byte(0) = revision.io;
|
||||
data.byte(1) = revision.rac;
|
||||
data.byte(2) = revision.rdp;
|
||||
data.byte(3) = revision.rsp;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//MI_INTR
|
||||
data.bit(0) = irq.sp.line;
|
||||
data.bit(1) = irq.si.line;
|
||||
data.bit(2) = irq.ai.line;
|
||||
data.bit(3) = irq.vi.line;
|
||||
data.bit(4) = irq.pi.line;
|
||||
data.bit(5) = irq.dp.line;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//MI_INTR_MASK
|
||||
data.bit(0) = irq.sp.mask;
|
||||
data.bit(1) = irq.si.mask;
|
||||
data.bit(2) = irq.ai.mask;
|
||||
data.bit(3) = irq.vi.mask;
|
||||
data.bit(4) = irq.pi.mask;
|
||||
data.bit(5) = irq.dp.mask;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "MI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto MI::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data = data_;
|
||||
|
||||
if(address == 0) {
|
||||
//MI_INIT_MODE
|
||||
if(data.bit(11)) mi.lower(MI::IRQ::DP);
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//MI_VERSION (read-only)
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//MI_INTR (read-only)
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//MI_INTR_MASK
|
||||
if(data.bit( 0)) irq.sp.mask = 0;
|
||||
if(data.bit( 1)) irq.sp.mask = 1;
|
||||
if(data.bit( 2)) irq.si.mask = 0;
|
||||
if(data.bit( 3)) irq.si.mask = 1;
|
||||
if(data.bit( 4)) irq.ai.mask = 0;
|
||||
if(data.bit( 5)) irq.ai.mask = 1;
|
||||
if(data.bit( 6)) irq.vi.mask = 0;
|
||||
if(data.bit( 7)) irq.vi.mask = 1;
|
||||
if(data.bit( 8)) irq.pi.mask = 0;
|
||||
if(data.bit( 9)) irq.pi.mask = 1;
|
||||
if(data.bit(10)) irq.dp.mask = 0;
|
||||
if(data.bit(11)) irq.dp.mask = 1;
|
||||
poll();
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "MI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
MI mi;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto MI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("MI");
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto MI::unload() -> void {
|
||||
node.reset();
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto MI::raise(IRQ source) -> void {
|
||||
if(debugger.tracer.interrupt->enabled()) {
|
||||
if(!irq.sp.line && source == IRQ::SP) debugger.interrupt("SP");
|
||||
if(!irq.si.line && source == IRQ::SI) debugger.interrupt("SI");
|
||||
if(!irq.ai.line && source == IRQ::AI) debugger.interrupt("AI");
|
||||
if(!irq.vi.line && source == IRQ::VI) debugger.interrupt("VI");
|
||||
if(!irq.pi.line && source == IRQ::PI) debugger.interrupt("PI");
|
||||
if(!irq.dp.line && source == IRQ::DP) debugger.interrupt("DP");
|
||||
}
|
||||
|
||||
switch(source) {
|
||||
case IRQ::SP: irq.sp.line = 1; break;
|
||||
case IRQ::SI: irq.si.line = 1; break;
|
||||
case IRQ::AI: irq.ai.line = 1; break;
|
||||
case IRQ::VI: irq.vi.line = 1; break;
|
||||
case IRQ::PI: irq.pi.line = 1; break;
|
||||
case IRQ::DP: irq.dp.line = 1; break;
|
||||
}
|
||||
poll();
|
||||
}
|
||||
|
||||
auto MI::lower(IRQ source) -> void {
|
||||
switch(source) {
|
||||
case IRQ::SP: irq.sp.line = 0; break;
|
||||
case IRQ::SI: irq.si.line = 0; break;
|
||||
case IRQ::AI: irq.ai.line = 0; break;
|
||||
case IRQ::VI: irq.vi.line = 0; break;
|
||||
case IRQ::PI: irq.pi.line = 0; break;
|
||||
case IRQ::DP: irq.dp.line = 0; break;
|
||||
}
|
||||
poll();
|
||||
}
|
||||
|
||||
auto MI::poll() -> void {
|
||||
bool line = 0;
|
||||
line |= irq.sp.line & irq.sp.mask;
|
||||
line |= irq.si.line & irq.si.mask;
|
||||
line |= irq.ai.line & irq.ai.mask;
|
||||
line |= irq.vi.line & irq.vi.mask;
|
||||
line |= irq.pi.line & irq.pi.mask;
|
||||
line |= irq.dp.line & irq.dp.mask;
|
||||
cpu.scc.cause.interruptPending.bit(2) = line;
|
||||
}
|
||||
|
||||
auto MI::power() -> void {
|
||||
irq.sp.line = 0; irq.sp.mask = 1;
|
||||
irq.si.line = 0; irq.si.mask = 1;
|
||||
irq.ai.line = 0; irq.ai.mask = 1;
|
||||
irq.vi.line = 0; irq.vi.mask = 1;
|
||||
irq.pi.line = 0; irq.pi.mask = 1;
|
||||
irq.dp.line = 0; irq.dp.mask = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//MIPS Interface
|
||||
|
||||
struct MI : Memory::IO<MI> {
|
||||
Node::Component node;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto interrupt(string_view) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification interrupt;
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
//mi.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
enum class IRQ : uint { SP, SI, AI, VI, PI, DP };
|
||||
auto raise(IRQ) -> void;
|
||||
auto lower(IRQ) -> void;
|
||||
auto poll() -> void;
|
||||
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Interrupt {
|
||||
bool line;
|
||||
bool mask;
|
||||
};
|
||||
|
||||
struct IRQs {
|
||||
Interrupt sp;
|
||||
Interrupt si;
|
||||
Interrupt ai;
|
||||
Interrupt vi;
|
||||
Interrupt pi;
|
||||
Interrupt dp;
|
||||
} irq;
|
||||
|
||||
struct IO {
|
||||
} io;
|
||||
|
||||
struct Revision {
|
||||
static constexpr u8 io = 0x02; //I/O interface
|
||||
static constexpr u8 rac = 0x01; //RAMBUS ASIC cell
|
||||
static constexpr u8 rdp = 0x02; //Reality Display Processor
|
||||
static constexpr u8 rsp = 0x02; //Reality Signal Processor
|
||||
} revision;
|
||||
};
|
||||
|
||||
extern MI mi;
|
||||
@@ -0,0 +1,2 @@
|
||||
auto MI::serialize(serializer& s) -> void {
|
||||
}
|
||||
+52
-4
@@ -2,19 +2,67 @@
|
||||
//started: 2020-04-28
|
||||
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
#include <component/processor/vr4300/vr4300.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/recompiler/amd64/amd64.hpp>
|
||||
#include <nmmintrin.h>
|
||||
using v128 = __m128i;
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
#include <ares/inline.hpp>
|
||||
struct Accuracy {
|
||||
//enable all accuracy flags
|
||||
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 {
|
||||
static inline auto NTSC() -> bool;
|
||||
static inline auto PAL() -> bool;
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
auto reset() -> void {
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer(clock);
|
||||
}
|
||||
|
||||
i64 clock;
|
||||
};
|
||||
|
||||
#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>
|
||||
#include <n64/pi/pi.hpp>
|
||||
#include <n64/ri/ri.hpp>
|
||||
#include <n64/si/si.hpp>
|
||||
#include <n64/rdram/rdram.hpp>
|
||||
#include <n64/cpu/cpu.hpp>
|
||||
#include <n64/rdp/rdp.hpp>
|
||||
#include <n64/rsp/rsp.hpp>
|
||||
#include <n64/memory/bus.hpp>
|
||||
}
|
||||
|
||||
#include <n64/interface/interface.hpp>
|
||||
@@ -0,0 +1,18 @@
|
||||
auto PI::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "PI");
|
||||
|
||||
memory.ram = parent->append<Node::Memory>("PI RAM");
|
||||
memory.ram->setSize(64);
|
||||
memory.ram->setRead([&](uint32 address) -> uint8 {
|
||||
return pi.ram.readByte(address);
|
||||
});
|
||||
memory.ram->setWrite([&](uint32 address, uint8 data) -> void {
|
||||
return pi.ram.writeByte(address, data);
|
||||
});
|
||||
}
|
||||
|
||||
auto PI::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
static const vector<string> registerNames = {
|
||||
"PI_DRAM_ADDRESS",
|
||||
"PI_PBUS_ADDRESS",
|
||||
"PI_READ_LENGTH",
|
||||
"PI_WRITE_LENGTH",
|
||||
"PI_STATUS",
|
||||
"PI_BSD_DOM1_LAT",
|
||||
"PI_BSD_DOM1_PWD",
|
||||
"PI_BSD_DOM1_PGS",
|
||||
"PI_BSD_DOM1_RLS",
|
||||
"PI_BSD_DOM2_LAT",
|
||||
"PI_BSD_DOM2_PWD",
|
||||
"PI_BSD_DOM2_PGS",
|
||||
"PI_BSD_DOM2_RLS",
|
||||
};
|
||||
|
||||
auto PI::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
uint32 data;
|
||||
|
||||
if(address == 0) {
|
||||
//PI_DRAM_ADDRESS
|
||||
data = io.dramAddress;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//PI_CART_ADDRESS
|
||||
data = io.pbusAddress;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//PI_READ_LENGTH
|
||||
data = io.readLength;
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//PI_WRITE_LENGTH
|
||||
data = io.writeLength;
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//PI_STATUS
|
||||
data.bit(0) = io.dmaBusy;
|
||||
data.bit(1) = io.ioBusy;
|
||||
data.bit(2) = io.error;
|
||||
data.bit(3) = io.interrupt;
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//PI_BSD_DOM1_LAT
|
||||
data.bit(0,7) = bsd1.latency;
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//PI_BSD_DOM1_PWD
|
||||
data.bit(0,7) = bsd1.pulseWidth;
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//PI_BSD_DOM1_PGS
|
||||
data.bit(0,7) = bsd1.pageSize;
|
||||
}
|
||||
|
||||
if(address == 8) {
|
||||
//PI_BSD_DOM1_RLS
|
||||
data.bit(0,7) = bsd1.releaseDuration;
|
||||
}
|
||||
|
||||
if(address == 9) {
|
||||
//PI_BSD_DOM2_LAT
|
||||
data.bit(0,7) = bsd2.latency;
|
||||
}
|
||||
|
||||
if(address == 10) {
|
||||
//PI_BSD_DOM2_PWD
|
||||
data.bit(0,7) = bsd2.pulseWidth;
|
||||
}
|
||||
|
||||
if(address == 11) {
|
||||
//PI_BSD_DOM2_PGS
|
||||
data.bit(0,7) = bsd2.pageSize;
|
||||
}
|
||||
|
||||
if(address == 12) {
|
||||
//PI_BSD_DOM2_RLS
|
||||
data.bit(0,7) = bsd2.releaseDuration;
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "PI_UNKNOWN"), " => ", hex(data, 8L)});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
auto PI::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)) {
|
||||
io.error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(address == 0) {
|
||||
//PI_DRAM_ADDRESS
|
||||
io.dramAddress = uint24(data) & ~7;
|
||||
}
|
||||
|
||||
if(address == 1) {
|
||||
//PI_PBUS_ADDRESS
|
||||
io.pbusAddress = uint29(data) & ~1;
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
//PI_READ_LENGTH
|
||||
io.readLength = (uint24(data) | 7) + 1;
|
||||
for(u32 address = 0; address < io.readLength; address += 2) {
|
||||
u16 data = bus.readHalf(io.dramAddress + address);
|
||||
bus.writeHalf(io.pbusAddress + address, data);
|
||||
}
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
if(address == 3) {
|
||||
//PI_WRITE_LENGTH
|
||||
io.writeLength = (uint24(data) | 7) + 1;
|
||||
for(u32 address = 0; address < io.writeLength; address += 2) {
|
||||
u16 data = bus.readHalf(io.pbusAddress + address);
|
||||
bus.writeHalf(io.dramAddress + address, data);
|
||||
}
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
if(address == 4) {
|
||||
//PI_STATUS
|
||||
if(data.bit(0)) io.error = 0;
|
||||
if(data.bit(1)) io.interrupt = 0, mi.lower(MI::IRQ::PI);
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
//PI_BSD_DOM1_LAT
|
||||
bsd1.latency = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 6) {
|
||||
//PI_BSD_DOM1_PWD
|
||||
bsd1.pulseWidth = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 7) {
|
||||
//PI_BSD_DOM1_PGS
|
||||
bsd1.pageSize = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 8) {
|
||||
//PI_BSD_DOM1_RLS
|
||||
bsd1.releaseDuration = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 9) {
|
||||
//PI_BSD_DOM2_LAT
|
||||
bsd2.latency = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 10) {
|
||||
//PI_BSD_DOM2_PWD
|
||||
bsd2.pulseWidth = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 11) {
|
||||
//PI_BSD_DOM2_PGS
|
||||
bsd2.pageSize = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(address == 12) {
|
||||
//PI_BSD_DOM2_RLS
|
||||
bsd2.releaseDuration = data.bit(0,7);
|
||||
}
|
||||
|
||||
if(debugger.tracer.io->enabled()) {
|
||||
debugger.io({registerNames(address, "PI_UNKNOWN"), " <= ", hex(data, 8L)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
PI pi;
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto PI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("PI");
|
||||
rom.allocate(0x7c0);
|
||||
ram.allocate(0x040);
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto PI::unload() -> void {
|
||||
rom.reset();
|
||||
ram.reset();
|
||||
node = {};
|
||||
debugger = {};
|
||||
}
|
||||
|
||||
auto PI::power() -> void {
|
||||
rom.fill();
|
||||
ram.fill();
|
||||
io = {};
|
||||
bsd1 = {};
|
||||
bsd2 = {};
|
||||
|
||||
string iplrom = cartridge.region() == "NTSC" ? "pif.ntsc.rom" : "pif.pal.rom";
|
||||
iplrom = "pif.rom";
|
||||
if(auto fp = platform->open(node, iplrom, File::Read, File::Required)) {
|
||||
rom.load(fp);
|
||||
}
|
||||
|
||||
//write CIC seeds into PIF RAM so that cartridge checksum function passes
|
||||
string cic = cartridge.cic();
|
||||
if(1) { //power
|
||||
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") ram.writeWord(0x24, 0x00043f3f);
|
||||
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") ram.writeWord(0x24, 0x00003f3f);
|
||||
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") ram.writeWord(0x24, 0x0000783f);
|
||||
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") ram.writeWord(0x24, 0x0000913f);
|
||||
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") ram.writeWord(0x24, 0x0000853f);
|
||||
}
|
||||
|
||||
if(0) { //reset
|
||||
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") ram.writeWord(0x24, 0x00063f3f);
|
||||
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") ram.writeWord(0x24, 0x00023f3f);
|
||||
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") ram.writeWord(0x24, 0x0002783f);
|
||||
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") ram.writeWord(0x24, 0x0002913f);
|
||||
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") ram.writeWord(0x24, 0x0002853f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//Peripheral Interface
|
||||
|
||||
struct PI : Memory::IO<PI> {
|
||||
Node::Component node;
|
||||
Memory::Readable rom;
|
||||
Memory::Writable ram;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto io(string_view) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Notification io;
|
||||
} tracer;
|
||||
|
||||
struct Memory {
|
||||
Node::Memory ram;
|
||||
} memory;
|
||||
} debugger;
|
||||
|
||||
//pi.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
uint1 dmaBusy = 0;
|
||||
uint1 ioBusy = 0;
|
||||
uint1 error = 0;
|
||||
uint1 interrupt = 0;
|
||||
uint32 dramAddress = 0;
|
||||
uint32 pbusAddress = 0;
|
||||
uint32 readLength = 0;
|
||||
uint32 writeLength = 0;
|
||||
} io;
|
||||
|
||||
struct BSD {
|
||||
uint8 latency = 0;
|
||||
uint8 pulseWidth = 0;
|
||||
uint8 pageSize = 0;
|
||||
uint8 releaseDuration = 0;
|
||||
} bsd1, bsd2;
|
||||
};
|
||||
|
||||
extern PI pi;
|
||||
@@ -0,0 +1,2 @@
|
||||
auto PI::serialize(serializer& s) -> void {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
auto RDP::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.command = parent->append<Node::Notification>("Command", "RDP");
|
||||
tracer.io = parent->append<Node::Notification>("I/O", "RDP");
|
||||
}
|
||||
|
||||
auto RDP::Debugger::command(string_view message) -> void {
|
||||
if(tracer.command->enabled()) {
|
||||
tracer.command->notify(message);
|
||||
}
|
||||
}
|
||||
|
||||
auto RDP::Debugger::io(string_view message) -> void {
|
||||
if(tracer.io->enabled()) {
|
||||
tracer.io->notify(message);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 333 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user