update ares, fix some lingering issues with recompiler
This commit is contained in:
parent
00152bbaa9
commit
02caa5fcbb
Binary file not shown.
|
@ -31,19 +31,13 @@ typedef enum
|
|||
START = 1 << 13,
|
||||
} Buttons_t;
|
||||
|
||||
static u64 biztime = 0;
|
||||
|
||||
static u64 GetBizTime()
|
||||
{
|
||||
return biztime;
|
||||
}
|
||||
|
||||
struct BizPlatform : ares::Platform
|
||||
{
|
||||
auto attach(ares::Node::Object) -> void override;
|
||||
auto pak(ares::Node::Object) -> ares::VFS::Pak override;
|
||||
auto audio(ares::Node::Audio::Stream) -> void override;
|
||||
auto input(ares::Node::Input::Input) -> void override;
|
||||
auto time() -> n64 override;
|
||||
|
||||
ares::VFS::Pak bizpak = nullptr;
|
||||
u16* soundbuf = alloc_invisible<u16>(1024 * 2);
|
||||
|
@ -51,6 +45,7 @@ struct BizPlatform : ares::Platform
|
|||
bool hack = false;
|
||||
void (*inputcb)() = nullptr;
|
||||
bool lagged = true;
|
||||
u64 biztime = 0;
|
||||
};
|
||||
|
||||
auto BizPlatform::attach(ares::Node::Object node) -> void
|
||||
|
@ -88,7 +83,12 @@ auto BizPlatform::input(ares::Node::Input::Input node) -> void
|
|||
if (inputcb) inputcb();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
auto BizPlatform::time() -> n64
|
||||
{
|
||||
return biztime;
|
||||
}
|
||||
|
||||
static ares::Node::System root = nullptr;
|
||||
static BizPlatform* platform = nullptr;
|
||||
|
@ -98,6 +98,7 @@ static array_view<u8>* romData = nullptr;
|
|||
static array_view<u8>* diskData = nullptr;
|
||||
static array_view<u8>* diskErrorData = nullptr;
|
||||
static array_view<u8>* saveData = nullptr;
|
||||
static array_view<u8>* rtcData = nullptr;
|
||||
static array_view<u8>* gbRomData[4] = { nullptr, nullptr, nullptr, nullptr, };
|
||||
|
||||
typedef enum
|
||||
|
@ -311,7 +312,8 @@ static inline SaveType DetectSaveType(u8* rom)
|
|||
if (id == "NW4") ret = FLASH128KB;
|
||||
if (id == "NDP") ret = FLASH128KB;
|
||||
|
||||
if(id[1] == 'E' && id[2] == 'D') {
|
||||
if (id[1] == 'E' && id[2] == 'D')
|
||||
{
|
||||
n8 config = revision;
|
||||
if (config.bit(4,7) == 1) ret = EEPROM512;
|
||||
else if (config.bit(4,7) == 2) ret = EEPROM2KB;
|
||||
|
@ -324,6 +326,26 @@ static inline SaveType DetectSaveType(u8* rom)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline bool DetectRtc(u8* rom)
|
||||
{
|
||||
string id;
|
||||
id.append((char)rom[0x3B]);
|
||||
id.append((char)rom[0x3C]);
|
||||
id.append((char)rom[0x3D]);
|
||||
|
||||
u8 revision = rom[0x3f];
|
||||
|
||||
if (id == "NAF") return true;
|
||||
|
||||
if (id[1] == 'E' && id[2] == 'D')
|
||||
{
|
||||
n8 config = revision;
|
||||
return config.bit(0) == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace ares::Nintendo64
|
||||
{
|
||||
extern bool BobDeinterlace;
|
||||
|
@ -351,16 +373,6 @@ typedef struct
|
|||
GbRom GbRoms[4];
|
||||
} LoadData;
|
||||
|
||||
#define SET_RTC_CALLBACK(NUM) do { \
|
||||
if (auto pad = dynamic_cast<ares::Nintendo64::Gamepad*>(ares::Nintendo64::controllerPort##NUM.device.data())) \
|
||||
{ \
|
||||
if (auto mbc3 = dynamic_cast<ares::Nintendo64::Mbc3*>(pad->transferPak.mbc.data())) \
|
||||
{ \
|
||||
mbc3->rtcCallback = GetBizTime; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool LoadRom(LoadData* loadData, bool isPal)
|
||||
{
|
||||
u8* data;
|
||||
|
@ -406,6 +418,15 @@ static bool LoadRom(LoadData* loadData, bool isPal)
|
|||
platform->bizpak->append(name, *saveData);
|
||||
}
|
||||
|
||||
if (DetectRtc(data))
|
||||
{
|
||||
len = 32, name = "save.rtc";
|
||||
data = new u8[len];
|
||||
memset(data, 0xFF, len);
|
||||
rtcData = new array_view<u8>(data, len);
|
||||
platform->bizpak->append(name, *rtcData);
|
||||
}
|
||||
|
||||
if (auto port = root->find<ares::Node::Port>("Cartridge Slot"))
|
||||
{
|
||||
port->allocate();
|
||||
|
@ -464,8 +485,7 @@ ECL_EXPORT bool Init(LoadData* loadData, ControllerType* controllers, bool isPal
|
|||
platform->bizpak = new vfs::directory;
|
||||
ares::platform = platform;
|
||||
|
||||
biztime = initTime;
|
||||
ares::Nintendo64::dd.rtcCallback = GetBizTime;
|
||||
platform->biztime = initTime;
|
||||
|
||||
angrylion::OutFrameBuffer = NULL;
|
||||
angrylion::OutHeight = isPal ? 576 : 480;
|
||||
|
@ -579,11 +599,6 @@ ECL_EXPORT bool Init(LoadData* loadData, ControllerType* controllers, bool isPal
|
|||
}
|
||||
}
|
||||
|
||||
SET_RTC_CALLBACK(1);
|
||||
SET_RTC_CALLBACK(2);
|
||||
SET_RTC_CALLBACK(3);
|
||||
SET_RTC_CALLBACK(4);
|
||||
|
||||
root->power(false);
|
||||
root->run(); // HACK, first frame dirties a ton of memory, so we emulate it then seal (this should be investigated, not sure why 60MBish of memory would be dirtied in a single frame?)
|
||||
return true;
|
||||
|
@ -682,15 +697,17 @@ static u8 PeekFunc(u64 address)
|
|||
}
|
||||
}
|
||||
|
||||
return ares::Nintendo64::bus.read<ares::Nintendo64::Byte>(addr);
|
||||
u32 unused = 0;
|
||||
return ares::Nintendo64::bus.read<ares::Nintendo64::Byte>(addr, unused);
|
||||
}
|
||||
|
||||
static void SysBusAccess(u8* buffer, u64 address, u64 count, bool write)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
u32 unused = 0;
|
||||
while (count--)
|
||||
ares::Nintendo64::bus.write<ares::Nintendo64::Byte>(address++, *buffer++);
|
||||
ares::Nintendo64::bus.write<ares::Nintendo64::Byte>(address++, *buffer++, unused);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -791,7 +808,7 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo* f)
|
|||
|
||||
angrylion::OutFrameBuffer = f->SkipDraw ? NULL : f->VideoBuffer;
|
||||
|
||||
biztime = f->Time;
|
||||
platform->biztime = f->Time;
|
||||
|
||||
if (f->Power)
|
||||
{
|
||||
|
@ -828,7 +845,10 @@ ECL_EXPORT void SetInputCallback(void (*callback)())
|
|||
|
||||
ECL_EXPORT void PostLoadState()
|
||||
{
|
||||
ares::Nintendo64::cpu.recompiler.allocator.release(bump_allocator::zero_fill);
|
||||
ares::Nintendo64::cpu.recompiler.reset();
|
||||
ares::Nintendo64::rsp.recompiler.allocator.release(bump_allocator::zero_fill);
|
||||
ares::Nintendo64::rsp.recompiler.reset();
|
||||
}
|
||||
|
||||
ECL_EXPORT void GetDisassembly(u32 address, u32 instruction, char* buf)
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
NEED_LIBCO := 1
|
||||
|
||||
ARES_PATH = $(ROOT_DIR)/ares/ares
|
||||
NALL_PATH = $(ROOT_DIR)/ares/nall
|
||||
THIRDPARTY_PATH = $(ROOT_DIR)/ares/thirdparty
|
||||
ANGRYLION_PATH = $(THIRDPARTY_PATH)/angrylion-rdp/mylittle-nocomment
|
||||
SLJIT_PATH = $(THIRDPARTY_PATH)/sljit/sljit_src
|
||||
|
||||
CCFLAGS := -march=x86-64-v2 -I.$(THIRDPARTY_PATH) -DSLJIT_HAVE_CONFIG_PRE=1 -DSLJIT_HAVE_CONFIG_POST=1
|
||||
|
||||
CXXFLAGS := -std=gnu++17 -march=x86-64-v2 \
|
||||
-I../libco -I.$(ROOT_DIR)/ares -I.$(ARES_PATH) -I.$(THIRDPARTY_PATH) -I.$(ANGRYLION_PATH) \
|
||||
-Werror=int-to-pointer-cast -Wno-unused-but-set-variable -Wno-format-security \
|
||||
|
@ -12,11 +15,14 @@ CXXFLAGS := -std=gnu++17 -march=x86-64-v2 \
|
|||
-Wno-sign-compare -Wno-switch -Wno-unused-local-typedefs -Wno-bool-operation \
|
||||
-Wno-mismatched-tags -Wno-missing-braces -Wno-overloaded-virtual \
|
||||
-Wno-unused-private-field -Wno-sometimes-uninitialized \
|
||||
-fno-strict-aliasing -fwrapv -fno-operator-names \
|
||||
-fno-strict-aliasing -fwrapv \
|
||||
-DSLJIT_HAVE_CONFIG_PRE=1 -DSLJIT_HAVE_CONFIG_POST=1
|
||||
|
||||
TARGET = ares64.wbx
|
||||
|
||||
SRCS_NALL = \
|
||||
$(NALL_PATH)/nall.cpp
|
||||
|
||||
SRCS_PROCESSORS = \
|
||||
$(ARES_PATH)/component/processor/sm5k/sm5k.cpp
|
||||
|
||||
|
@ -28,6 +34,7 @@ SRCS_N64 = \
|
|||
$(ARES_PATH)/n64/memory/memory.cpp \
|
||||
$(ARES_PATH)/n64/system/system.cpp \
|
||||
$(ARES_PATH)/n64/cartridge/cartridge.cpp \
|
||||
$(ARES_PATH)/n64/cic/cic.cpp \
|
||||
$(ARES_PATH)/n64/controller/controller.cpp \
|
||||
$(ARES_PATH)/n64/dd/dd.cpp \
|
||||
$(ARES_PATH)/n64/mi/mi.cpp \
|
||||
|
@ -50,6 +57,6 @@ SRCS_SLJIT = \
|
|||
$(SLJIT_PATH)/sljitLir.c \
|
||||
$(THIRDPARTY_PATH)/sljitAllocator.cpp
|
||||
|
||||
SRCS = $(SRCS_PROCESSORS) $(SRCS_ARES) $(SRCS_N64) $(SRCS_ANGRYLION) $(SRCS_SLJIT) BizInterface.cpp
|
||||
SRCS = $(SRCS_NALL) $(SRCS_PROCESSORS) $(SRCS_ARES) $(SRCS_N64) $(SRCS_ANGRYLION) $(SRCS_SLJIT) BizInterface.cpp
|
||||
|
||||
include ../common.mak
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <nall/any.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bump-allocator.hpp>
|
||||
#include <nall/case-range.hpp>
|
||||
#include <nall/chrono.hpp>
|
||||
#include <nall/directory.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
|
@ -38,10 +39,11 @@
|
|||
#include <nall/hash/crc32.hpp>
|
||||
#include <nall/hash/sha256.hpp>
|
||||
using namespace nall;
|
||||
using namespace nall::primitives;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "130.1";
|
||||
static const string Version = "132";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
@ -50,7 +52,6 @@ namespace ares {
|
|||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
static const string SerializerVersion = "130.3";
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace ares::Memory {
|
|||
FixedAllocator::FixedAllocator() {
|
||||
}
|
||||
#else
|
||||
alignas(4096) u8 fixedBuffer[8_MiB];
|
||||
alignas(4096) u8 fixedBuffer[128_MiB] ECL_INVISIBLE;
|
||||
|
||||
FixedAllocator::FixedAllocator() {
|
||||
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
|
||||
|
|
|
@ -25,7 +25,7 @@ struct Stream : Audio {
|
|||
template<typename... P>
|
||||
auto frame(P&&... p) -> void {
|
||||
if(runAhead()) return;
|
||||
f64 samples[sizeof...(p)] = {std::forward<P>(p)...};
|
||||
f64 samples[sizeof...(p)] = {f64(std::forward<P>(p))...};
|
||||
write(samples);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ struct Instruction : Tracer {
|
|||
auto address(u64 address) -> bool {
|
||||
address &= ~0ull >> (64 - _addressBits); //mask upper bits of address
|
||||
_address = address;
|
||||
/*address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
|
||||
if(_mask) {
|
||||
auto mask = _masks.find(address);
|
||||
|
@ -50,7 +50,7 @@ struct Instruction : Tracer {
|
|||
_history[index] = _history[index + 1];
|
||||
}
|
||||
_history.last() = _address;
|
||||
}*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -58,13 +58,13 @@ struct Instruction : Tracer {
|
|||
//mark an already-executed address as not executed yet for trace masking.
|
||||
//call when writing to executable RAM to support self-modifying code.
|
||||
auto invalidate(u64 address) -> void {
|
||||
/*if(unlikely(_mask)) {
|
||||
if(unlikely(_mask)) {
|
||||
address &= ~0ull >> (64 - _addressBits);
|
||||
address >>= _addressMask;
|
||||
|
||||
auto mask = _masks.find(address);
|
||||
if(mask) mask->unvisit(address);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
auto notify(const string& instruction, const string& context, const string& extra = {}) -> void {
|
||||
|
|
|
@ -20,6 +20,7 @@ struct Platform {
|
|||
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
|
||||
virtual auto audio(Node::Audio::Stream) -> void {}
|
||||
virtual auto input(Node::Input::Input) -> void {}
|
||||
virtual auto time() -> n64 { return ::time(0); }
|
||||
};
|
||||
|
||||
extern Platform* platform;
|
||||
|
|
|
@ -12,54 +12,54 @@ auto SM5K::disassembleInstruction() -> string {
|
|||
string pc = {"0x", hex(n4(opcode) << 8 | operand, 3L)};
|
||||
|
||||
switch(opcode) {
|
||||
case 0x00 ... 0x0f: s = {"adx ", p4}; break;
|
||||
case 0x10 ... 0x1f: s = {"lax ", p4}; break;
|
||||
case 0x20 ... 0x2f: s = {"lblx ", p4}; break;
|
||||
case 0x30 ... 0x3f: s = {"lbmx ", p4}; break;
|
||||
case 0x40 ... 0x43: s = {"rm ", p2}; break;
|
||||
case 0x44 ... 0x47: s = {"sm ", p2}; break;
|
||||
case 0x48 ... 0x4b: s = {"tm ", p2}; break;
|
||||
case 0x4c ... 0x4f: s = {"tpb ", p2}; break;
|
||||
case 0x50 ... 0x53: s = {"lda ", p2}; break;
|
||||
case 0x54 ... 0x57: s = {"exc ", p2}; break;
|
||||
case 0x58 ... 0x5b: s = {"exci ", p2}; break;
|
||||
case 0x5c ... 0x5f: s = {"excd ", p2}; break;
|
||||
case 0x60: s = {"rc " }; break;
|
||||
case 0x61: s = {"sc " }; break;
|
||||
case 0x62: s = {"id " }; break;
|
||||
case 0x63: s = {"ie " }; break;
|
||||
case 0x64: s = {"exax " }; break;
|
||||
case 0x65: s = {"atx " }; break;
|
||||
case 0x66: s = {"exbm " }; break;
|
||||
case 0x67: s = {"exbl " }; break;
|
||||
case 0x68: s = {"ex " }; break;
|
||||
case 0x69: s = {"dta ", p8}; break;
|
||||
case 0x6a: s = {"pat ", p8}; break;
|
||||
case 0x6b: s = {"tabl " }; break;
|
||||
case 0x6c: s = {"ta " }; break;
|
||||
case 0x6d: s = {"tb " }; break;
|
||||
case 0x6e: s = {"tc " }; break;
|
||||
case 0x6f: s = {"tam " }; break;
|
||||
case 0x70: s = {"inl " }; break;
|
||||
case 0x71: s = {"outl " }; break;
|
||||
case 0x72: s = {"anp " }; break;
|
||||
case 0x73: s = {"orp " }; break;
|
||||
case 0x74: s = {"in " }; break;
|
||||
case 0x75: s = {"out " }; break;
|
||||
case 0x76: s = {"stop " }; break;
|
||||
case 0x77: s = {"halt " }; break;
|
||||
case 0x78: s = {"incb " }; break;
|
||||
case 0x79: s = {"coma " }; break;
|
||||
case 0x7a: s = {"add " }; break;
|
||||
case 0x7b: s = {"adc " }; break;
|
||||
case 0x7c: s = {"decb " }; break;
|
||||
case 0x7d: s = {"rtn " }; break;
|
||||
case 0x7e: s = {"rtns " }; break;
|
||||
case 0x7f: s = {"rtni " }; break;
|
||||
case 0x80 ... 0xbf: s = {"tr ", p6}; break;
|
||||
case 0xc0 ... 0xdf: s = {"trs ", p5}; break;
|
||||
case 0xe0 ... 0xef: s = {"tl ", pc}; break;
|
||||
case 0xf0 ... 0xff: s = {"call ", pc}; break;
|
||||
case range16(0x00, 0x0f): s = {"adx ", p4}; break;
|
||||
case range16(0x10, 0x1f): s = {"lax ", p4}; break;
|
||||
case range16(0x20, 0x2f): s = {"lblx ", p4}; break;
|
||||
case range16(0x30, 0x3f): s = {"lbmx ", p4}; break;
|
||||
case range4 (0x40, 0x43): s = {"rm ", p2}; break;
|
||||
case range4 (0x44, 0x47): s = {"sm ", p2}; break;
|
||||
case range4 (0x48, 0x4b): s = {"tm ", p2}; break;
|
||||
case range4 (0x4c, 0x4f): s = {"tpb ", p2}; break;
|
||||
case range4 (0x50, 0x53): s = {"lda ", p2}; break;
|
||||
case range4 (0x54, 0x57): s = {"exc ", p2}; break;
|
||||
case range4 (0x58, 0x5b): s = {"exci ", p2}; break;
|
||||
case range4 (0x5c, 0x5f): s = {"excd ", p2}; break;
|
||||
case 0x60: s = {"rc " }; break;
|
||||
case 0x61: s = {"sc " }; break;
|
||||
case 0x62: s = {"id " }; break;
|
||||
case 0x63: s = {"ie " }; break;
|
||||
case 0x64: s = {"exax " }; break;
|
||||
case 0x65: s = {"atx " }; break;
|
||||
case 0x66: s = {"exbm " }; break;
|
||||
case 0x67: s = {"exbl " }; break;
|
||||
case 0x68: s = {"ex " }; break;
|
||||
case 0x69: s = {"dta ", p8}; break;
|
||||
case 0x6a: s = {"pat ", p8}; break;
|
||||
case 0x6b: s = {"tabl " }; break;
|
||||
case 0x6c: s = {"ta " }; break;
|
||||
case 0x6d: s = {"tb " }; break;
|
||||
case 0x6e: s = {"tc " }; break;
|
||||
case 0x6f: s = {"tam " }; break;
|
||||
case 0x70: s = {"inl " }; break;
|
||||
case 0x71: s = {"outl " }; break;
|
||||
case 0x72: s = {"anp " }; break;
|
||||
case 0x73: s = {"orp " }; break;
|
||||
case 0x74: s = {"in " }; break;
|
||||
case 0x75: s = {"out " }; break;
|
||||
case 0x76: s = {"stop " }; break;
|
||||
case 0x77: s = {"halt " }; break;
|
||||
case 0x78: s = {"incb " }; break;
|
||||
case 0x79: s = {"coma " }; break;
|
||||
case 0x7a: s = {"add " }; break;
|
||||
case 0x7b: s = {"adc " }; break;
|
||||
case 0x7c: s = {"decb " }; break;
|
||||
case 0x7d: s = {"rtn " }; break;
|
||||
case 0x7e: s = {"rtns " }; break;
|
||||
case 0x7f: s = {"rtni " }; break;
|
||||
case range64(0x80, 0xbf): s = {"tr ", p6}; break;
|
||||
case range32(0xc0, 0xdf): s = {"trs ", p5}; break;
|
||||
case range16(0xe0, 0xef): s = {"tl ", pc}; break;
|
||||
case range16(0xf0, 0xff): s = {"call ", pc}; break;
|
||||
}
|
||||
|
||||
while(s.size() < 10) s.append(" ");
|
||||
|
|
|
@ -20,54 +20,54 @@ auto SM5K::instruction() -> void {
|
|||
|
||||
n8 opcode = fetch();
|
||||
switch(opcode) {
|
||||
op(0x00 ... 0x0f, ADX, n4(opcode));
|
||||
op(0x10 ... 0x1f, LAX, n4(opcode));
|
||||
op(0x20 ... 0x2f, LBLX, n4(opcode));
|
||||
op(0x30 ... 0x3f, LBMX, n4(opcode));
|
||||
op(0x40 ... 0x43, RM, n2(opcode));
|
||||
op(0x44 ... 0x47, SM, n2(opcode));
|
||||
op(0x48 ... 0x4b, TM, n2(opcode));
|
||||
op(0x4c ... 0x4f, TPB, n2(opcode));
|
||||
op(0x50 ... 0x53, LDA, n2(opcode));
|
||||
op(0x54 ... 0x57, EXC, n2(opcode));
|
||||
op(0x58 ... 0x5b, EXCI, n2(opcode));
|
||||
op(0x5c ... 0x5f, EXCD, n2(opcode));
|
||||
op(0x60, RC );
|
||||
op(0x61, SC );
|
||||
op(0x62, ID );
|
||||
op(0x63, IE );
|
||||
op(0x64, EXAX );
|
||||
op(0x65, ATX );
|
||||
op(0x66, EXBM );
|
||||
op(0x67, EXBL );
|
||||
op(0x68, EX );
|
||||
op(0x69, DTA, fetch());
|
||||
op(0x6a, PAT, fetch());
|
||||
op(0x6b, TABL );
|
||||
op(0x6c, TA );
|
||||
op(0x6d, TB );
|
||||
op(0x6e, TC );
|
||||
op(0x6f, TAM );
|
||||
op(0x70, INL );
|
||||
op(0x71, OUTL );
|
||||
op(0x72, ANP );
|
||||
op(0x73, ORP );
|
||||
op(0x74, IN );
|
||||
op(0x75, OUT );
|
||||
op(0x76, STOP );
|
||||
op(0x77, HALT );
|
||||
op(0x78, INCB );
|
||||
op(0x79, COMA );
|
||||
op(0x7a, ADD );
|
||||
op(0x7b, ADC );
|
||||
op(0x7c, DECB );
|
||||
op(0x7d, RTN );
|
||||
op(0x7e, RTNS );
|
||||
op(0x7f, RTNI );
|
||||
op(0x80 ... 0xbf, TR, n6(opcode));
|
||||
op(0xc0 ... 0xdf, TRS, n5(opcode));
|
||||
op(0xe0 ... 0xef, TL, n4(opcode) << 8 | fetch());
|
||||
op(0xf0 ... 0xff, CALL, n4(opcode) << 8 | fetch());
|
||||
op(range16(0x00, 0x0f), ADX, n4(opcode));
|
||||
op(range16(0x10, 0x1f), LAX, n4(opcode));
|
||||
op(range16(0x20, 0x2f), LBLX, n4(opcode));
|
||||
op(range16(0x30, 0x3f), LBMX, n4(opcode));
|
||||
op(range4 (0x40, 0x43), RM, n2(opcode));
|
||||
op(range4 (0x44, 0x47), SM, n2(opcode));
|
||||
op(range4 (0x48, 0x4b), TM, n2(opcode));
|
||||
op(range4 (0x4c, 0x4f), TPB, n2(opcode));
|
||||
op(range4 (0x50, 0x53), LDA, n2(opcode));
|
||||
op(range4 (0x54, 0x57), EXC, n2(opcode));
|
||||
op(range4 (0x58, 0x5b), EXCI, n2(opcode));
|
||||
op(range4 (0x5c, 0x5f), EXCD, n2(opcode));
|
||||
op(0x60, RC );
|
||||
op(0x61, SC );
|
||||
op(0x62, ID );
|
||||
op(0x63, IE );
|
||||
op(0x64, EXAX );
|
||||
op(0x65, ATX );
|
||||
op(0x66, EXBM );
|
||||
op(0x67, EXBL );
|
||||
op(0x68, EX );
|
||||
op(0x69, DTA, fetch());
|
||||
op(0x6a, PAT, fetch());
|
||||
op(0x6b, TABL );
|
||||
op(0x6c, TA );
|
||||
op(0x6d, TB );
|
||||
op(0x6e, TC );
|
||||
op(0x6f, TAM );
|
||||
op(0x70, INL );
|
||||
op(0x71, OUTL );
|
||||
op(0x72, ANP );
|
||||
op(0x73, ORP );
|
||||
op(0x74, IN );
|
||||
op(0x75, OUT );
|
||||
op(0x76, STOP );
|
||||
op(0x77, HALT );
|
||||
op(0x78, INCB );
|
||||
op(0x79, COMA );
|
||||
op(0x7a, ADD );
|
||||
op(0x7b, ADC );
|
||||
op(0x7c, DECB );
|
||||
op(0x7d, RTN );
|
||||
op(0x7e, RTNS );
|
||||
op(0x7f, RTNI );
|
||||
op(range64(0x80, 0xbf), TR, n6(opcode));
|
||||
op(range32(0xc0, 0xdf), TRS, n5(opcode));
|
||||
op(range16(0xe0, 0xef), TL, n4(opcode) << 8 | fetch());
|
||||
op(range16(0xf0, 0xff), CALL, n4(opcode) << 8 | fetch());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ struct Accuracy {
|
|||
static constexpr bool Reference = 0;
|
||||
|
||||
struct CPU {
|
||||
static constexpr bool Interpreter = 0 | Reference;
|
||||
static constexpr bool Interpreter = 0 | Reference | !recompiler::generic::supported;
|
||||
static constexpr bool Recompiler = !Interpreter;
|
||||
|
||||
//exceptions when the CPU accesses unaligned memory addresses
|
||||
|
@ -11,7 +11,7 @@ struct Accuracy {
|
|||
};
|
||||
|
||||
struct RSP {
|
||||
static constexpr bool Interpreter = 0 | Reference;
|
||||
static constexpr bool Interpreter = 0 | Reference | !recompiler::generic::supported;
|
||||
static constexpr bool Recompiler = !Interpreter;
|
||||
|
||||
//VU instructions
|
||||
|
@ -22,4 +22,9 @@ struct Accuracy {
|
|||
struct RDRAM {
|
||||
static constexpr bool Broadcasting = 0;
|
||||
};
|
||||
|
||||
struct PIF {
|
||||
// Emulate a region-locked console
|
||||
static constexpr bool RegionLock = false;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Audio Interface
|
||||
|
||||
struct AI : Thread, Memory::IO<AI> {
|
||||
struct AI : Thread, Memory::RCP<AI> {
|
||||
Node::Object node;
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
|
@ -23,8 +23,8 @@ struct AI : Thread, Memory::IO<AI> {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto AI::readWord(u32 address) -> u32 {
|
||||
auto AI::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -21,7 +21,7 @@ auto AI::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto AI::writeWord(u32 address, u32 data_) -> void {
|
||||
auto AI::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace ares::Nintendo64 {
|
|||
Cartridge& cartridge = cartridgeSlot.cartridge;
|
||||
#include "slot.cpp"
|
||||
#include "flash.cpp"
|
||||
#include "rtc.cpp"
|
||||
#include "joybus.cpp"
|
||||
#include "isviewer.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
@ -43,6 +45,8 @@ auto Cartridge::connect() -> void {
|
|||
flash.load(fp);
|
||||
}
|
||||
|
||||
rtc.load();
|
||||
|
||||
isviewer.ram.allocate(64_KiB);
|
||||
|
||||
debugger.load(node);
|
||||
|
@ -77,6 +81,8 @@ auto Cartridge::save() -> void {
|
|||
if(auto fp = pak->write("save.flash")) {
|
||||
flash.save(fp);
|
||||
}
|
||||
|
||||
rtc.save();
|
||||
}
|
||||
|
||||
auto Cartridge::power(bool reset) -> void {
|
||||
|
@ -85,6 +91,7 @@ auto Cartridge::power(bool reset) -> void {
|
|||
flash.source = 0;
|
||||
flash.offset = 0;
|
||||
isviewer.ram.fill(0);
|
||||
rtc.power(reset);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,14 +38,38 @@ struct Cartridge {
|
|||
u32 source = 0;
|
||||
u32 offset = 0;
|
||||
} flash;
|
||||
struct ISViewer : Memory::IO<ISViewer> {
|
||||
struct ISViewer : Memory::PI<ISViewer> {
|
||||
Memory::Writable ram; //unserialized
|
||||
|
||||
//isviewer.cpp
|
||||
auto readHalf(u32 address) -> u16;
|
||||
auto writeHalf(u32 address, u16 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
} isviewer;
|
||||
|
||||
struct RTC {
|
||||
Cartridge& self;
|
||||
RTC(Cartridge &self) : self(self) {}
|
||||
|
||||
Memory::Writable ram;
|
||||
n1 present;
|
||||
n8 status;
|
||||
n3 writeLock;
|
||||
|
||||
// rtc.cpp
|
||||
auto power(bool reset) -> void;
|
||||
auto run(bool run) -> void;
|
||||
auto running() -> bool;
|
||||
auto load() -> void;
|
||||
auto save() -> void;
|
||||
auto tick(int nsec=1) -> void;
|
||||
auto advance(int nsec) -> void;
|
||||
auto serialize(serializer& s) -> void;
|
||||
auto read(u2 block, n8 *data) -> void;
|
||||
auto write(u2 block, n8 *data) -> void;
|
||||
} rtc{*this};
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
|
@ -67,10 +91,12 @@ struct Cartridge {
|
|||
auto allocate(Node::Port) -> Node::Peripheral;
|
||||
auto connect() -> void;
|
||||
auto disconnect() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//joybus.cpp
|
||||
auto joybusComm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
|
|
|
@ -1,22 +1,39 @@
|
|||
auto Cartridge::ISViewer::readWord(u32 address) -> u32 {
|
||||
u32 data = ram.read<Word>(address);
|
||||
address = (address & 0xffff) >> 2;
|
||||
|
||||
if(address == 0) {
|
||||
data = 0x49533634; //'IS64'
|
||||
}
|
||||
|
||||
return data;
|
||||
auto Cartridge::ISViewer::readHalf(u32 address) -> u16 {
|
||||
address = (address & 0xffff);
|
||||
return ram.read<Half>(address);
|
||||
}
|
||||
|
||||
auto Cartridge::ISViewer::writeWord(u32 address, u32 data) -> void {
|
||||
ram.write<Word>(address, data);
|
||||
address = (address & 0xffff) >> 2;
|
||||
auto Cartridge::ISViewer::readWord(u32 address) -> u32 {
|
||||
address = (address & 0xffff);
|
||||
return ram.read<Word>(address);
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
for(auto address : range(u16(data))) {
|
||||
auto Cartridge::ISViewer::writeHalf(u32 address, u16 data) -> void {
|
||||
address = (address & 0xffff);
|
||||
|
||||
if(address == 0x16) {
|
||||
// HACK: allow printf output to work for both libultra and libdragon
|
||||
// Libultra expects a real IS-Viewer device and treats this address as a
|
||||
// pointer to the end of the buffer, reading the current value, writing N
|
||||
// bytes, then updating the buffer pointer.
|
||||
// libdragon instead treats this as a "number of bytes" register, only
|
||||
// writing an "output byte count"
|
||||
// In order to satisfy both libraries, we assume it behaves as libdragon
|
||||
// expects, and by forcing the write to never hit ram, libultra remains
|
||||
// functional.
|
||||
for(auto address : range(data)) {
|
||||
char c = ram.read<Byte>(0x20 + address);
|
||||
fputc(c, stdout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ram.write<Half>(address, data);
|
||||
}
|
||||
|
||||
auto Cartridge::ISViewer::writeWord(u32 address, u32 data) -> void {
|
||||
address = (address & 0xffff);
|
||||
writeHalf(address+0, data >> 16);
|
||||
writeHalf(address+2, data & 0xffff);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
auto Cartridge::joybusComm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
|
||||
n1 valid = 0, over = 0;
|
||||
|
||||
//status
|
||||
if(input[0] == 0x00 || input[0] == 0xff) {
|
||||
//cartridge EEPROM (4kbit)
|
||||
if(cartridge.eeprom.size == 512) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0x80;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//cartridge EEPROM (16kbit)
|
||||
if(cartridge.eeprom.size == 2048) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0xc0;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//read EEPROM
|
||||
if(input[0] == 0x04 && send >= 2) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(recv)) {
|
||||
output[index] = cartridge.eeprom.read<Byte>(address++);
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//write EEPROM
|
||||
if(input[0] == 0x05 && send >= 2 && recv >= 1) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(send - 2)) {
|
||||
cartridge.eeprom.write<Byte>(address++, input[2 + index]);
|
||||
}
|
||||
output[0] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//RTC status
|
||||
if(input[0] == 0x06 && send >= 1 && recv >= 3) {
|
||||
if(cartridge.rtc.present) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0x10;
|
||||
output[2] = rtc.status;
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//RTC read
|
||||
if(input[0] == 0x07 && send >= 2 && recv >= 9) {
|
||||
if(cartridge.rtc.present) {
|
||||
rtc.read(input[1], &output[0]);
|
||||
output[8] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//RTC write
|
||||
if(input[0] == 0x08 && send >= 10 && recv >= 1) {
|
||||
if(cartridge.rtc.present) {
|
||||
rtc.write(input[1], &input[2]);
|
||||
output[0] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
n2 status;
|
||||
status.bit(0) = valid;
|
||||
status.bit(1) = over;
|
||||
return status;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
|
||||
auto Cartridge::RTC::power(bool reset) -> void {
|
||||
if(present) run(!status.bit(7));
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::load() -> void {
|
||||
if(auto fp = self.pak->read("save.rtc")) {
|
||||
ram.allocate(fp->size());
|
||||
ram.load(fp);
|
||||
|
||||
present = 1;
|
||||
n64 timestamp = ram.read<Dual>(24);
|
||||
if(!~timestamp) {
|
||||
ram.fill(0);
|
||||
ram.write<Byte>(21, 1);
|
||||
}
|
||||
|
||||
timestamp = platform->time() - timestamp;
|
||||
advance(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::save() -> void {
|
||||
#if false
|
||||
if(auto fp = self.pak->write("save.rtc")) {
|
||||
ram.write<Dual>(24, time(0));
|
||||
ram.save(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::tick(int nsec) -> void {
|
||||
advance(nsec);
|
||||
run(true);
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::run(bool run) -> void {
|
||||
status.bit(7) = !run;
|
||||
queue.remove(Queue::RTC_Tick);
|
||||
if(run) queue.insert(Queue::RTC_Tick, 187'500'000);
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::running() -> bool {
|
||||
return !status.bit(7);
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::advance(int nsec) -> void {
|
||||
struct tm tmm = {};
|
||||
tmm.tm_sec = BCD::decode(ram.read<Byte>(16));
|
||||
tmm.tm_min = BCD::decode(ram.read<Byte>(17));
|
||||
tmm.tm_hour = BCD::decode(ram.read<Byte>(18) & 0x7f);
|
||||
tmm.tm_mday = BCD::decode(ram.read<Byte>(19));
|
||||
tmm.tm_mon = BCD::decode(ram.read<Byte>(21)) - 1;
|
||||
tmm.tm_year = BCD::decode(ram.read<Byte>(22)) + 100 * BCD::decode(ram.read<Byte>(23));
|
||||
time_t t = mktime(&tmm);
|
||||
|
||||
t += nsec;
|
||||
|
||||
tmm = *localtime(&t);
|
||||
ram.write<Byte>(16, BCD::encode(tmm.tm_sec));
|
||||
ram.write<Byte>(17, BCD::encode(tmm.tm_min));
|
||||
ram.write<Byte>(18, BCD::encode(tmm.tm_hour) | 0x80);
|
||||
ram.write<Byte>(19, BCD::encode(tmm.tm_mday));
|
||||
ram.write<Byte>(20, BCD::encode(tmm.tm_wday));
|
||||
ram.write<Byte>(21, BCD::encode(tmm.tm_mon + 1));
|
||||
ram.write<Byte>(22, BCD::encode(tmm.tm_year % 100));
|
||||
ram.write<Byte>(23, BCD::encode(tmm.tm_year / 100));
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::read(u2 block, n8* data) -> void {
|
||||
data[0] = ram.read<Byte>(block*8 + 0);
|
||||
data[1] = ram.read<Byte>(block*8 + 1);
|
||||
data[2] = ram.read<Byte>(block*8 + 2);
|
||||
data[3] = ram.read<Byte>(block*8 + 3);
|
||||
data[4] = ram.read<Byte>(block*8 + 4);
|
||||
data[5] = ram.read<Byte>(block*8 + 5);
|
||||
data[6] = ram.read<Byte>(block*8 + 6);
|
||||
data[7] = ram.read<Byte>(block*8 + 7);
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::write(u2 block, n8* data) -> void {
|
||||
if (writeLock.bit(block)) return;
|
||||
ram.write<Byte>(block*8 + 0, data[0]);
|
||||
ram.write<Byte>(block*8 + 1, data[1]);
|
||||
ram.write<Byte>(block*8 + 2, data[2]);
|
||||
ram.write<Byte>(block*8 + 3, data[3]);
|
||||
ram.write<Byte>(block*8 + 4, data[4]);
|
||||
ram.write<Byte>(block*8 + 5, data[5]);
|
||||
ram.write<Byte>(block*8 + 6, data[6]);
|
||||
ram.write<Byte>(block*8 + 7, data[7]);
|
||||
if(block == 0) {
|
||||
n16 control = ram.read<Half>(0);
|
||||
writeLock.bit(1,2) = control.bit(8,9);
|
||||
run(!control.bit(2));
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::RTC::serialize(serializer& s) -> void {
|
||||
s(ram);
|
||||
s(status);
|
||||
s(writeLock);
|
||||
}
|
|
@ -2,4 +2,5 @@ auto Cartridge::serialize(serializer& s) -> void {
|
|||
s(ram);
|
||||
s(eeprom);
|
||||
s(flash);
|
||||
s(rtc);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
#include <n64/n64.hpp>
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
CIC cic;
|
||||
#include "io.cpp"
|
||||
#include "commands.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto CIC::power(bool reset) -> void {
|
||||
model = cartridge.node ? cartridge.cic() : dd.cic();
|
||||
type = Cartridge;
|
||||
challengeAlgo = DummyChallenge;
|
||||
if(model == "CIC-NUS-6101") region = NTSC, seed = 0x3f, checksum = 0x45cc73ee317aull;
|
||||
if(model == "CIC-NUS-6102") region = NTSC, seed = 0x3f, checksum = 0xa536c0f1d859ull;
|
||||
if(model == "CIC-NUS-7101") region = PAL, seed = 0x3f, checksum = 0xa536c0f1d859ull;
|
||||
if(model == "CIC-NUS-7102") region = PAL, seed = 0x3f, checksum = 0x44160ec5d9afull;
|
||||
if(model == "CIC-NUS-6103") region = NTSC, seed = 0x78, checksum = 0x586fd4709867ull;
|
||||
if(model == "CIC-NUS-7103") region = PAL, seed = 0x78, checksum = 0x586fd4709867ull;
|
||||
if(model == "CIC-NUS-6105") region = NTSC, seed = 0x91, checksum = 0x8618a45bc2d3ull, challengeAlgo = RealChallenge;
|
||||
if(model == "CIC-NUS-7105") region = PAL, seed = 0x91, checksum = 0x8618a45bc2d3ull, challengeAlgo = RealChallenge;
|
||||
if(model == "CIC-NUS-6106") region = NTSC, seed = 0x85, checksum = 0x2bbad4e6eb74ull;
|
||||
if(model == "CIC-NUS-7106") region = PAL, seed = 0x85, checksum = 0x2bbad4e6eb74ull;
|
||||
if(model == "CIC-NUS-8303") region = NTSC, seed = 0xdd, checksum = 0x32b294e2ab90ull, type = DD64;
|
||||
if(model == "CIC-NUS-8401") region = NTSC, seed = 0xdd, checksum = 0x6ee8d9e84970ull, type = DD64;
|
||||
if(model == "CIC-NUS-5167") region = NTSC, seed = 0xdd, checksum = 0x083c6c77e0b1ull;
|
||||
if(model == "CIC-NUS-DDUS") region = NTSC, seed = 0xde, checksum = 0x05ba2ef0a5f1ull, type = DD64;
|
||||
state = BootRegion;
|
||||
fifo.bits.resize(32*4);
|
||||
}
|
||||
|
||||
auto CIC::scramble(n4 *buf, int size) -> void {
|
||||
for(int i : range(1,size)) buf[i] += buf[i-1] + 1;
|
||||
}
|
||||
|
||||
auto CIC::poll() -> void {
|
||||
if(state == BootRegion) {
|
||||
fifo.write(type);
|
||||
fifo.write(region == PAL);
|
||||
fifo.write(0);
|
||||
fifo.write(1);
|
||||
state = BootSeed;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == BootSeed) {
|
||||
n4 buf[6];
|
||||
buf[0] = 0xB;
|
||||
buf[1] = 0x5;
|
||||
buf[2] = seed.bit(4,7);
|
||||
buf[3] = seed.bit(0,3);
|
||||
buf[4] = seed.bit(4,7);
|
||||
buf[5] = seed.bit(0,3);
|
||||
for (auto i : range(2)) scramble(buf, 6);
|
||||
for (auto i : range(6)) fifo.writeNibble(buf[i]);
|
||||
state = BootChecksum;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == BootChecksum) {
|
||||
n4 buf[16];
|
||||
buf[0] = 0x4; //true random
|
||||
buf[1] = 0x7; //true random
|
||||
buf[2] = 0xA; //true random
|
||||
buf[3] = 0x1; //true random
|
||||
for (auto i : range(12)) buf[i+4] = checksum.bit(44-i*4,47-i*4);
|
||||
for (auto i : range(4)) scramble(buf, 16);
|
||||
for (auto i : range(16)) fifo.writeNibble(buf[i]);
|
||||
state = Run;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == Run && fifo.size() >= 2) {
|
||||
n2 cmd;
|
||||
cmd.bit(1) = fifo.read();
|
||||
cmd.bit(0) = fifo.read();
|
||||
if(cmd == 0b00) return cmdCompare();
|
||||
if(cmd == 0b01) return cmdDie();
|
||||
if(cmd == 0b10) return cmdChallenge();
|
||||
if(cmd == 0b11) return cmdReset();
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == Challenge) {
|
||||
return cmdChallenge();
|
||||
}
|
||||
|
||||
if(state == Dead) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
struct CIC {
|
||||
enum State : u32 { BootRegion, BootSeed, BootChecksum, Run, Challenge, Dead };
|
||||
enum Region : u32 { NTSC, PAL };
|
||||
enum ChallengeAlgo : bool { DummyChallenge, RealChallenge };
|
||||
enum Type : u32 { Cartridge, DD64 };
|
||||
|
||||
struct {
|
||||
nall::queue<n1> bits;
|
||||
|
||||
auto empty() -> bool { return bits.empty(); }
|
||||
auto size() -> u32 { return bits.size(); }
|
||||
auto write(n1 data) -> void { bits.write(data); }
|
||||
auto read() -> n1 { return bits.read(); }
|
||||
auto writeNibble(n4 data) -> void {
|
||||
write(data.bit(3));
|
||||
write(data.bit(2));
|
||||
write(data.bit(1));
|
||||
write(data.bit(0));
|
||||
}
|
||||
auto readNibble() -> n4 {
|
||||
n4 data;
|
||||
data.bit(3) = read();
|
||||
data.bit(2) = read();
|
||||
data.bit(1) = read();
|
||||
data.bit(0) = read();
|
||||
return data;
|
||||
}
|
||||
} fifo;
|
||||
n8 seed;
|
||||
n48 checksum; //ipl2 checksum
|
||||
n1 type;
|
||||
n1 region;
|
||||
n1 challengeAlgo;
|
||||
u32 state;
|
||||
string model;
|
||||
|
||||
//cic.cpp
|
||||
auto power(bool reset) -> void;
|
||||
auto poll() -> void;
|
||||
auto scramble(n4 *buf, int size) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readBit() -> n1;
|
||||
auto readNibble() -> n4;
|
||||
auto writeBit(n1 cmd) -> void;
|
||||
auto writeNibble(n4 cmd) -> void;
|
||||
|
||||
//commands.cpp
|
||||
auto cmdCompare() -> void;
|
||||
auto cmdDie() -> void;
|
||||
auto cmdChallenge() -> void;
|
||||
auto cmdReset() -> void;
|
||||
auto challenge(n4 data[30]) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
|
||||
extern CIC cic;
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
auto CIC::cmdCompare() -> void {
|
||||
}
|
||||
|
||||
auto CIC::challenge(n4 mem[30]) -> void {
|
||||
if(challengeAlgo == DummyChallenge) {
|
||||
for(u32 address : range(30))
|
||||
mem[address] = ~mem[address];
|
||||
return;
|
||||
}
|
||||
|
||||
//CIC-NUS-6105 anti-piracy challenge
|
||||
if(challengeAlgo == RealChallenge) {
|
||||
static n4 lut[32] = {
|
||||
0x4, 0x7, 0xa, 0x7, 0xe, 0x5, 0xe, 0x1,
|
||||
0xc, 0xf, 0x8, 0xf, 0x6, 0x3, 0x6, 0x9,
|
||||
0x4, 0x1, 0xa, 0x7, 0xe, 0x5, 0xe, 0x1,
|
||||
0xc, 0x9, 0x8, 0x5, 0x6, 0x3, 0xc, 0x9,
|
||||
};
|
||||
|
||||
n4 key = 0xb;
|
||||
n1 sel = 0;
|
||||
for(u32 address : range(30)) {
|
||||
n4 data = key + 5 * mem[address];
|
||||
mem[address] = data;
|
||||
key = lut[sel << 4 | data];
|
||||
n1 mod = data >> 3;
|
||||
n3 mag = data >> 0;
|
||||
if(mod) mag = ~mag;
|
||||
if(mag % 3 != 1) mod = !mod;
|
||||
if(sel) {
|
||||
if(data == 0x1 || data == 0x9) mod = 1;
|
||||
if(data == 0xb || data == 0xe) mod = 0;
|
||||
}
|
||||
sel = mod;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto CIC::cmdChallenge() -> void {
|
||||
if(state == Run) {
|
||||
fifo.writeNibble(0xa);
|
||||
fifo.writeNibble(0xa);
|
||||
state = Challenge;
|
||||
}
|
||||
if(state == Challenge && fifo.size() == 30*4) {
|
||||
n4 data[30];
|
||||
for (auto i : range(30)) data[i] = fifo.readNibble();
|
||||
challenge(data);
|
||||
fifo.write(0); // write 0 bit
|
||||
for (auto i : range(30)) fifo.writeNibble(data[i]);
|
||||
state = Run;
|
||||
printf("CIC challenge complete %d\n", fifo.size());
|
||||
}
|
||||
}
|
||||
|
||||
auto CIC::cmdDie() -> void {
|
||||
debug(unusual, "[CIC::cmdDie] die command received by PIF");
|
||||
state = Dead;
|
||||
}
|
||||
|
||||
auto CIC::cmdReset() -> void {
|
||||
debug(unimplemented, "[CIC::cmdReset]");
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
auto CIC::writeBit(n1 data) -> void {
|
||||
fifo.write(data);
|
||||
poll();
|
||||
}
|
||||
|
||||
auto CIC::writeNibble(n4 data) -> void {
|
||||
fifo.writeNibble(data);
|
||||
poll();
|
||||
}
|
||||
|
||||
auto CIC::readBit() -> n1 {
|
||||
if(fifo.empty()) cic.poll();
|
||||
return fifo.read();
|
||||
}
|
||||
|
||||
auto CIC::readNibble() -> n4 {
|
||||
if (fifo.empty()) cic.poll();
|
||||
return fifo.readNibble();
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
auto CIC::serialize(serializer& s) -> void {
|
||||
s(fifo.bits);
|
||||
s(seed);
|
||||
s(checksum);
|
||||
s(type);
|
||||
s(region);
|
||||
s(challengeAlgo);
|
||||
s(state);
|
||||
}
|
|
@ -4,6 +4,7 @@ struct Controller {
|
|||
virtual ~Controller() = default;
|
||||
virtual auto save() -> void {}
|
||||
virtual auto comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 { return 1; }
|
||||
virtual auto reset() -> void {}
|
||||
virtual auto read() -> n32 { return 0; }
|
||||
virtual auto serialize(serializer&) -> void {}
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ Gamepad::~Gamepad() {
|
|||
}
|
||||
|
||||
auto Gamepad::save() -> void {
|
||||
/*
|
||||
#if false
|
||||
if(!slot) return;
|
||||
if(slot->name() == "Controller Pak") {
|
||||
ram.save(pak->write("save.pak"));
|
||||
|
@ -43,7 +43,7 @@ auto Gamepad::save() -> void {
|
|||
transferPak.ram.save(pak->write("gbram.pak"));
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
auto Gamepad::allocate(string name) -> Node::Peripheral {
|
||||
|
@ -135,36 +135,41 @@ auto Gamepad::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
|
|||
if(input[0] == 0x02 && send >= 3 && recv >= 1) {
|
||||
//controller pak
|
||||
if(ram) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = ram.read<Byte>(address++);
|
||||
if(address <= 0x7FFF) output[index] = ram.read<Byte>(address);
|
||||
else output[index] = 0;
|
||||
address++;
|
||||
}
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1});
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1u});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//rumble pak
|
||||
if(motor) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = 0x80;
|
||||
if(address <= 0x7FFF) output[index] = 0;
|
||||
else if(address <= 0x8FFF) output[index] = 0x80;
|
||||
else output[index] = motor->enable() ? 0xFF : 0x00;
|
||||
address++;
|
||||
}
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1});
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1u});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//transfer pak
|
||||
if(transferPak) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(recv - 1)) {
|
||||
output[index] = transferPak.read(address++);
|
||||
}
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1});
|
||||
output[recv - 1] = pif.dataCRC({&output[0], recv - 1u});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
@ -174,34 +179,35 @@ auto Gamepad::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
|
|||
if(input[0] == 0x03 && send >= 3 && recv >= 1) {
|
||||
//controller pak
|
||||
if(ram) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(send - 3)) {
|
||||
ram.write<Byte>(address++, input[3 + index]);
|
||||
if(address <= 0x7FFF) ram.write<Byte>(address, input[3 + index]);
|
||||
address++;
|
||||
}
|
||||
output[0] = pif.dataCRC({&input[3], send - 3});
|
||||
output[0] = pif.dataCRC({&input[3], send - 3u});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//rumble pak
|
||||
if(motor) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
output[0] = pif.dataCRC({&input[3], send - 3});
|
||||
output[0] = pif.dataCRC({&input[3], send - 3u});
|
||||
valid = 1;
|
||||
rumble(input[3] & 1);
|
||||
if(address >= 0xC000) rumble(input[3] & 1);
|
||||
}
|
||||
}
|
||||
|
||||
//transfer pak
|
||||
if(transferPak) {
|
||||
u32 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
u16 address = (input[1] << 8 | input[2] << 0) & ~31;
|
||||
if(pif.addressCRC(address) == (n5)input[2]) {
|
||||
for(u32 index : range(send - 3)) {
|
||||
transferPak.write(address++, input[3 + index]);
|
||||
}
|
||||
output[0] = pif.dataCRC({&input[3], send - 3});
|
||||
output[0] = pif.dataCRC({&input[3], send - 3u});
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
@ -231,6 +237,47 @@ auto Gamepad::read() -> n32 {
|
|||
platform->input(z);
|
||||
platform->input(start);
|
||||
|
||||
#if false
|
||||
//scale {-32768 ... +32767} to {-85 ... +85}
|
||||
auto ax = x->value() * 85.0 / 32767.0;
|
||||
auto ay = y->value() * 85.0 / 32767.0;
|
||||
|
||||
//create inner axial dead-zone in range {-7 ... +7} and scale from it up to outer circular dead-zone of radius 85
|
||||
auto length = sqrt(ax * ax + ay * ay);
|
||||
if(length <= 85.0) {
|
||||
auto lengthAbsoluteX = abs(ax);
|
||||
auto lengthAbsoluteY = abs(ay);
|
||||
if(lengthAbsoluteX <= 7.0) {
|
||||
lengthAbsoluteX = 0.0;
|
||||
} else {
|
||||
lengthAbsoluteX = (lengthAbsoluteX - 7.0) * 85.0 / (85.0 - 7.0) / lengthAbsoluteX;
|
||||
}
|
||||
ax *= lengthAbsoluteX;
|
||||
if(lengthAbsoluteY <= 7.0) {
|
||||
lengthAbsoluteY = 0.0;
|
||||
} else {
|
||||
lengthAbsoluteY = (lengthAbsoluteY - 7.0) * 85.0 / (85.0 - 7.0) / lengthAbsoluteY;
|
||||
}
|
||||
ay *= lengthAbsoluteY;
|
||||
} else {
|
||||
length = 85.0 / length;
|
||||
ax *= length;
|
||||
ay *= length;
|
||||
}
|
||||
|
||||
//bound diagonals to an octagonal range {-69 ... +69}
|
||||
if(ax != 0.0 && ay != 0.0) {
|
||||
auto slope = ay / ax;
|
||||
auto edgex = copysign(85.0 / (abs(slope) + 16.0 / 69.0), ax);
|
||||
auto edgey = copysign(min(abs(edgex * slope), 85.0 / (1.0 / abs(slope) + 16.0 / 69.0)), ay);
|
||||
edgex = edgey / slope;
|
||||
|
||||
auto scale = sqrt(edgex * edgex + edgey * edgey) / 85.0;
|
||||
ax *= scale;
|
||||
ay *= scale;
|
||||
}
|
||||
#endif
|
||||
|
||||
n32 data;
|
||||
data.byte(0) = y->value();
|
||||
data.byte(1) = x->value();
|
||||
|
|
|
@ -10,6 +10,11 @@ protected:
|
|||
Memory::Writable& ram;
|
||||
};
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu-case-range"
|
||||
|
||||
#include "mbc1.hpp"
|
||||
#include "mbc3.hpp"
|
||||
#include "mbc5.hpp"
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
|
|
@ -2,7 +2,7 @@ struct Mbc3 : Mbc {
|
|||
explicit Mbc3(Memory::Readable& rom_, Memory::Writable& ram_, bool rtc) : Mbc(rom_, ram_), hasRtc(rtc) { reset(); }
|
||||
|
||||
inline auto rtcUpdate() -> void {
|
||||
u64 diff = rtcCallback() - lastTime;
|
||||
u64 diff = platform->time() - lastTime;
|
||||
lastTime += diff;
|
||||
if(!rtcHalt) {
|
||||
s8 seconds = rtcSeconds;
|
||||
|
@ -146,6 +146,4 @@ private:
|
|||
n8 rtcLatches[5] = {};
|
||||
|
||||
u64 lastTime = 0;
|
||||
public:
|
||||
std::function<u64()> rtcCallback = []() { return 0; };
|
||||
};
|
||||
|
|
|
@ -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->setDisconnect([&] { device.reset(); });
|
||||
port->setSupported({"Gamepad", "Mouse"});
|
||||
}
|
||||
|
||||
|
@ -25,7 +26,6 @@ auto ControllerPort::save() -> void {
|
|||
}
|
||||
|
||||
auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
device = {};
|
||||
if(name == "Gamepad") device = new Gamepad(port);
|
||||
if(name == "Mouse" ) device = new Mouse(port);
|
||||
if(device) return device->node;
|
||||
|
|
|
@ -2,7 +2,7 @@ template <typename T>
|
|||
auto CPU::roundNearest(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_ARM64)
|
||||
return vrndns_f32(f);
|
||||
#elif defined(ARCHITECTURE_AMD64)
|
||||
#elif defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_NEAREST_INT);
|
||||
return _mm_cvtss_f32(t);
|
||||
|
@ -14,9 +14,9 @@ auto CPU::roundNearest(f32 f) -> T {
|
|||
template <typename T>
|
||||
auto CPU::roundNearest(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_ARM64)
|
||||
float64x1_t vf = {f};
|
||||
return vrndn_f64(vf)[0];
|
||||
#elif defined(ARCHITECTURE_AMD64)
|
||||
float64x1_t vf = vdup_n_f64(f);
|
||||
return vget_lane_f64(vrndn_f64(vf), 0);
|
||||
#elif defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_NEAREST_INT);
|
||||
return _mm_cvtsd_f64(t);
|
||||
|
@ -27,7 +27,7 @@ auto CPU::roundNearest(f64 f) -> T {
|
|||
|
||||
template <typename T>
|
||||
auto CPU::roundCeil(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_POS_INF);
|
||||
return _mm_cvtss_f32(t);
|
||||
|
@ -38,7 +38,7 @@ auto CPU::roundCeil(f32 f) -> T {
|
|||
|
||||
template <typename T>
|
||||
auto CPU::roundCeil(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_POS_INF);
|
||||
return _mm_cvtsd_f64(t);
|
||||
|
@ -49,7 +49,7 @@ auto CPU::roundCeil(f64 f) -> T {
|
|||
|
||||
template<typename T>
|
||||
auto CPU::roundCurrent(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
auto t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_CUR_DIRECTION);
|
||||
return _mm_cvtss_f32(t);
|
||||
|
@ -60,7 +60,7 @@ auto CPU::roundCurrent(f32 f) -> T {
|
|||
|
||||
template<typename T>
|
||||
auto CPU::roundCurrent(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
auto t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_CUR_DIRECTION);
|
||||
return _mm_cvtsd_f64(t);
|
||||
|
@ -71,7 +71,7 @@ auto CPU::roundCurrent(f64 f) -> T {
|
|||
|
||||
template <typename T>
|
||||
auto CPU::roundFloor(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_NEG_INF);
|
||||
return _mm_cvtss_f32(t);
|
||||
|
@ -82,7 +82,7 @@ auto CPU::roundFloor(f32 f) -> T {
|
|||
|
||||
template <typename T>
|
||||
auto CPU::roundFloor(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_NEG_INF);
|
||||
return _mm_cvtsd_f64(t);
|
||||
|
@ -93,7 +93,7 @@ auto CPU::roundFloor(f64 f) -> T {
|
|||
|
||||
template <typename T>
|
||||
auto CPU::roundTrunc(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_ZERO);
|
||||
return _mm_cvtss_f32(t);
|
||||
|
@ -104,7 +104,7 @@ auto CPU::roundTrunc(f32 f) -> T {
|
|||
|
||||
template <typename T>
|
||||
auto CPU::roundTrunc(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#if defined(ARCHITECTURE_AMD64) && ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_ZERO);
|
||||
return _mm_cvtsd_f64(t);
|
||||
|
|
|
@ -46,10 +46,12 @@ auto CPU::synchronize() -> void {
|
|||
ai.clock -= clocks;
|
||||
rsp.clock -= clocks;
|
||||
rdp.clock -= clocks;
|
||||
pif.clock -= clocks;
|
||||
while( vi.clock < 0) vi.main();
|
||||
while( ai.clock < 0) ai.main();
|
||||
while(rsp.clock < 0) rsp.main();
|
||||
while(rdp.clock < 0) rdp.main();
|
||||
while(pif.clock < 0) pif.main();
|
||||
|
||||
queue.step(clocks, [](u32 event) {
|
||||
switch(event) {
|
||||
|
@ -59,6 +61,8 @@ auto CPU::synchronize() -> void {
|
|||
case Queue::PI_BUS_Write: return pi.writeFinished();
|
||||
case Queue::SI_DMA_Read: return si.dmaRead();
|
||||
case Queue::SI_DMA_Write: return si.dmaWrite();
|
||||
case Queue::SI_BUS_Write: return si.writeFinished();
|
||||
case Queue::RTC_Tick: return cartridge.rtc.tick();
|
||||
case Queue::DD_Clock_Tick: return dd.rtcTickClock();
|
||||
case Queue::DD_MECHA_Response: return dd.mechaResponse();
|
||||
case Queue::DD_BM_Request: return dd.bmRequest();
|
||||
|
@ -81,16 +85,24 @@ auto CPU::instruction() -> void {
|
|||
return exception.interrupt();
|
||||
}
|
||||
}
|
||||
if (scc.nmiPending) {
|
||||
debugger.nmi();
|
||||
step(1);
|
||||
return exception.nmi();
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
auto address = devirtualize(ipu.pc)(0);
|
||||
auto& block = recompiler.block(address);
|
||||
block.execute(*this);
|
||||
if (auto address = devirtualize(ipu.pc)) {
|
||||
auto block = recompiler.block(*address);
|
||||
block->execute(*this);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::CPU::Interpreter) {
|
||||
pipeline.address = ipu.pc;
|
||||
pipeline.instruction = fetch(ipu.pc);
|
||||
auto data = fetch(ipu.pc);
|
||||
if (!data) return;
|
||||
pipeline.instruction = *data;
|
||||
debugger.instruction();
|
||||
decoderEXECUTE();
|
||||
instructionEpilogue();
|
||||
|
@ -143,8 +155,8 @@ auto CPU::power(bool reset) -> void {
|
|||
context.setMode();
|
||||
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(4_MiB);
|
||||
recompiler.allocator.resize(4_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
recompiler.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ struct CPU : Thread {
|
|||
auto instruction() -> void;
|
||||
auto exception(u8 code) -> void;
|
||||
auto interrupt(u8 mask) -> void;
|
||||
auto nmi() -> void;
|
||||
auto tlbWrite(u32 index) -> void;
|
||||
auto tlbModification(u64 address) -> void;
|
||||
auto tlbLoad(u64 address, u64 physical) -> void;
|
||||
|
@ -83,7 +84,7 @@ struct CPU : Thread {
|
|||
|
||||
enum Endian : bool { Little, Big };
|
||||
enum Mode : u32 { Kernel, Supervisor, User };
|
||||
enum Segment : u32 { Unused, Mapped, Cached, Direct, Kernel64, Supervisor64, User64 };
|
||||
enum Segment : u32 { Unused, Mapped, Cached, Direct, Cached32, Direct32, Kernel64, Supervisor64, User64 };
|
||||
|
||||
auto littleEndian() const -> bool { return endian == Endian::Little; }
|
||||
auto bigEndian() const -> bool { return endian == Endian::Big; }
|
||||
|
@ -147,26 +148,26 @@ struct CPU : Thread {
|
|||
cpu.step(48);
|
||||
valid = 1;
|
||||
tag = address & ~0x0000'0fff;
|
||||
words[0] = bus.read<Word>(tag | index | 0x00);
|
||||
words[1] = bus.read<Word>(tag | index | 0x04);
|
||||
words[2] = bus.read<Word>(tag | index | 0x08);
|
||||
words[3] = bus.read<Word>(tag | index | 0x0c);
|
||||
words[4] = bus.read<Word>(tag | index | 0x10);
|
||||
words[5] = bus.read<Word>(tag | index | 0x14);
|
||||
words[6] = bus.read<Word>(tag | index | 0x18);
|
||||
words[7] = bus.read<Word>(tag | index | 0x1c);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x00);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x04);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x08);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0x0c);
|
||||
words[4] = cpu.busRead<Word>(tag | index | 0x10);
|
||||
words[5] = cpu.busRead<Word>(tag | index | 0x14);
|
||||
words[6] = cpu.busRead<Word>(tag | index | 0x18);
|
||||
words[7] = cpu.busRead<Word>(tag | index | 0x1c);
|
||||
}
|
||||
|
||||
auto writeBack(CPU& cpu) -> void {
|
||||
cpu.step(48);
|
||||
bus.write<Word>(tag | index | 0x00, words[0]);
|
||||
bus.write<Word>(tag | index | 0x04, words[1]);
|
||||
bus.write<Word>(tag | index | 0x08, words[2]);
|
||||
bus.write<Word>(tag | index | 0x0c, words[3]);
|
||||
bus.write<Word>(tag | index | 0x10, words[4]);
|
||||
bus.write<Word>(tag | index | 0x14, words[5]);
|
||||
bus.write<Word>(tag | index | 0x18, words[6]);
|
||||
bus.write<Word>(tag | index | 0x1c, words[7]);
|
||||
cpu.busWrite<Word>(tag | index | 0x00, words[0]);
|
||||
cpu.busWrite<Word>(tag | index | 0x04, words[1]);
|
||||
cpu.busWrite<Word>(tag | index | 0x08, words[2]);
|
||||
cpu.busWrite<Word>(tag | index | 0x0c, words[3]);
|
||||
cpu.busWrite<Word>(tag | index | 0x10, words[4]);
|
||||
cpu.busWrite<Word>(tag | index | 0x14, words[5]);
|
||||
cpu.busWrite<Word>(tag | index | 0x18, words[6]);
|
||||
cpu.busWrite<Word>(tag | index | 0x1c, words[7]);
|
||||
}
|
||||
|
||||
auto read(u32 address) const -> u32 { return words[address >> 2 & 7]; }
|
||||
|
@ -259,9 +260,12 @@ struct CPU : Thread {
|
|||
|
||||
auto segment(u64 vaddr) -> Context::Segment;
|
||||
auto devirtualize(u64 vaddr) -> maybe<u64>;
|
||||
auto fetch(u64 vaddr) -> u32;
|
||||
auto fetch(u64 vaddr) -> maybe<u32>;
|
||||
template<u32 Size> auto busWrite(u32 address, u64 data) -> void;
|
||||
template<u32 Size> auto busRead(u32 address) -> u64;
|
||||
template<u32 Size> auto read(u64 vaddr) -> maybe<u64>;
|
||||
template<u32 Size> auto write(u64 vaddr, u64 data) -> bool;
|
||||
template<u32 Size> auto vaddrAlignedError(u64 vaddr, bool write) -> bool;
|
||||
auto addressException(u64 vaddr) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
@ -296,6 +300,7 @@ struct CPU : Thread {
|
|||
auto trap() -> void;
|
||||
auto floatingPoint() -> void;
|
||||
auto watchAddress() -> void;
|
||||
auto nmi() -> void;
|
||||
} exception{*this};
|
||||
|
||||
enum Interrupt : u32 {
|
||||
|
@ -317,8 +322,6 @@ struct CPU : Thread {
|
|||
struct { int64_t s64; };
|
||||
struct { uint64_t u64; };
|
||||
struct { float64_t f64; };
|
||||
auto s128() const -> int128_t { return (int128_t)s64; }
|
||||
auto u128() const -> uint128_t { return (uint128_t)u64; }
|
||||
};
|
||||
using cr64 = const r64;
|
||||
|
||||
|
@ -594,6 +597,7 @@ struct CPU : Thread {
|
|||
|
||||
//other
|
||||
n64 latch;
|
||||
n1 nmiPending;
|
||||
} scc;
|
||||
|
||||
//interpreter-scc.cpp
|
||||
|
@ -821,8 +825,7 @@ struct CPU : Thread {
|
|||
};
|
||||
|
||||
struct Pool {
|
||||
Block blocks[1 << 6];
|
||||
bool dirty;
|
||||
Block* blocks[1 << 6];
|
||||
};
|
||||
|
||||
auto reset() -> void {
|
||||
|
@ -842,14 +845,13 @@ struct CPU : Thread {
|
|||
auto pool = pools[address >> 8 & 0x1fffff];
|
||||
if(!pool) return;
|
||||
memory::jitprotect(false);
|
||||
pool->blocks[address >> 2 & 0x3f].code = nullptr;
|
||||
pool->blocks[address >> 2 & 0x3f] = nullptr;
|
||||
memory::jitprotect(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto invalidatePool(u32 address) -> void {
|
||||
auto pool = pools[address >> 8 & 0x1fffff];
|
||||
if(pool && pool->dirty) memory::fill(pool, sizeof(Pool));
|
||||
pools[address >> 8 & 0x1fffff] = nullptr;
|
||||
}
|
||||
|
||||
auto invalidateRange(u32 address, u32 length) -> void {
|
||||
|
@ -859,9 +861,9 @@ struct CPU : Thread {
|
|||
}
|
||||
|
||||
auto pool(u32 address) -> Pool*;
|
||||
auto block(u32 address) -> Block&;
|
||||
auto block(u32 address) -> Block*;
|
||||
|
||||
auto emit(u32 address, Block& block) -> void;
|
||||
auto emit(u32 address) -> Block*;
|
||||
auto emitEXECUTE(u32 instruction) -> bool;
|
||||
auto emitSPECIAL(u32 instruction) -> bool;
|
||||
auto emitREGIMM(u32 instruction) -> bool;
|
||||
|
|
|
@ -11,21 +11,21 @@ template<u32 Size> auto CPU::DataCache::Line::fill(u32 address, u64 data) -> voi
|
|||
switch(address & 8) {
|
||||
case 0:
|
||||
if constexpr(Size != Dual) {
|
||||
words[0] = bus.read<Word>(tag | index | 0x0);
|
||||
words[1] = bus.read<Word>(tag | index | 0x4);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
}
|
||||
write<Size>(address, data);
|
||||
words[2] = bus.read<Word>(tag | index | 0x8);
|
||||
words[3] = bus.read<Word>(tag | index | 0xc);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
break;
|
||||
case 8:
|
||||
if constexpr(Size != Dual) {
|
||||
words[2] = bus.read<Word>(tag | index | 0x8);
|
||||
words[3] = bus.read<Word>(tag | index | 0xc);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
}
|
||||
write<Size>(address, data);
|
||||
words[0] = bus.read<Word>(tag | index | 0x0);
|
||||
words[1] = bus.read<Word>(tag | index | 0x4);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -38,16 +38,16 @@ auto CPU::DataCache::Line::fill(u32 address) -> void {
|
|||
//read words according to critical doubleword first scheme
|
||||
switch(address & 8) {
|
||||
case 0:
|
||||
words[0] = bus.read<Word>(tag | index | 0x0);
|
||||
words[1] = bus.read<Word>(tag | index | 0x4);
|
||||
words[2] = bus.read<Word>(tag | index | 0x8);
|
||||
words[3] = bus.read<Word>(tag | index | 0xc);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
break;
|
||||
case 8:
|
||||
words[2] = bus.read<Word>(tag | index | 0x8);
|
||||
words[3] = bus.read<Word>(tag | index | 0xc);
|
||||
words[0] = bus.read<Word>(tag | index | 0x0);
|
||||
words[1] = bus.read<Word>(tag | index | 0x4);
|
||||
words[2] = cpu.busRead<Word>(tag | index | 0x8);
|
||||
words[3] = cpu.busRead<Word>(tag | index | 0xc);
|
||||
words[0] = cpu.busRead<Word>(tag | index | 0x0);
|
||||
words[1] = cpu.busRead<Word>(tag | index | 0x4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -55,10 +55,10 @@ auto CPU::DataCache::Line::fill(u32 address) -> void {
|
|||
auto CPU::DataCache::Line::writeBack() -> void {
|
||||
cpu.step(40);
|
||||
dirty = 0;
|
||||
bus.write<Word>(tag | index | 0x0, words[0]);
|
||||
bus.write<Word>(tag | index | 0x4, words[1]);
|
||||
bus.write<Word>(tag | index | 0x8, words[2]);
|
||||
bus.write<Word>(tag | index | 0xc, words[3]);
|
||||
cpu.busWrite<Word>(tag | index | 0x0, words[0]);
|
||||
cpu.busWrite<Word>(tag | index | 0x4, words[1]);
|
||||
cpu.busWrite<Word>(tag | index | 0x8, words[2]);
|
||||
cpu.busWrite<Word>(tag | index | 0xc, words[3]);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::line(u32 address) -> Line& {
|
||||
|
|
|
@ -69,6 +69,12 @@ auto CPU::Debugger::interrupt(u8 mask) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto CPU::Debugger::nmi() -> void {
|
||||
if(unlikely(tracer.exception->enabled())) {
|
||||
tracer.exception->notify("NMI");
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::Debugger::tlbWrite(u32 index) -> void {
|
||||
if(unlikely(tracer.tlb->enabled())) {
|
||||
auto entry = cpu.tlb.entry[index & 31];
|
||||
|
|
|
@ -476,6 +476,6 @@ auto CPU::Disassembler::ccrRegisterValue(u32 index) const -> string {
|
|||
|
||||
template<typename... P>
|
||||
auto CPU::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
|
||||
if(showColors) return {terminal::csi, "0m", terminal::csi, "37m", std::forward<P>(p)..., terminal::csi, "0m"};
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
auto CPU::Exception::trigger(u32 code, u32 coprocessor, bool tlbMiss) -> void {
|
||||
self.debugger.exception(code);
|
||||
|
||||
u64 vectorBase = !self.scc.status.vectorLocation ? 0x8000'0000 : 0xbfc0'0200;
|
||||
if(self.context.bits == 64) vectorBase = (s32)vectorBase;
|
||||
u64 vectorBase = !self.scc.status.vectorLocation ? (s32)0x8000'0000 : (s32)0xbfc0'0200;
|
||||
|
||||
u16 vectorOffset = 0x0180;
|
||||
if(tlbMiss) {
|
||||
|
@ -51,3 +50,12 @@ auto CPU::Exception::arithmeticOverflow() -> void { trigger(12); }
|
|||
auto CPU::Exception::trap() -> void { trigger(13); }
|
||||
auto CPU::Exception::floatingPoint() -> void { trigger(15); }
|
||||
auto CPU::Exception::watchAddress() -> void { trigger(23); }
|
||||
|
||||
auto CPU::Exception::nmi() -> void {
|
||||
self.scc.status.vectorLocation = 1;
|
||||
self.scc.status.tlbShutdown = 0;
|
||||
self.scc.status.softReset = 0;
|
||||
self.scc.status.errorLevel = 1;
|
||||
self.scc.epcError = self.ipu.pc;
|
||||
self.ipu.pc = 0xffff'ffff'bfc0'0000;
|
||||
}
|
||||
|
|
|
@ -208,15 +208,13 @@ auto CPU::checkFPUExceptions() -> bool {
|
|||
return raise;
|
||||
}
|
||||
|
||||
#define CHECK_FPE_IMPL(type, operation, convert) ({ \
|
||||
#define CHECK_FPE_IMPL(type, res, operation, convert) \
|
||||
fenv.clearExcept(); \
|
||||
type res = [&]() noinline { return type(operation); }(); \
|
||||
if (checkFPUExceptions<convert>()) return; \
|
||||
(res); \
|
||||
})
|
||||
type res = [&]() noinline -> type { return operation; }(); \
|
||||
if (checkFPUExceptions<convert>()) return;
|
||||
|
||||
#define CHECK_FPE(type, operation) CHECK_FPE_IMPL(type, operation, false)
|
||||
#define CHECK_FPE_CONV(type, operation) CHECK_FPE_IMPL(type, operation, true)
|
||||
#define CHECK_FPE(type, res, operation) CHECK_FPE_IMPL(type, res, operation, false)
|
||||
#define CHECK_FPE_CONV(type, res, operation) CHECK_FPE_IMPL(type, res, operation, true)
|
||||
|
||||
auto f32repr(f32 f) -> n32 {
|
||||
uint32_t v; memcpy(&v, &f, 4);
|
||||
|
@ -420,7 +418,7 @@ auto CPU::FADD_S(u8 fd, u8 fs, u8 ft) -> void {
|
|||
f32 ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
float ffd = CHECK_FPE(f32, FS(f32) + FT(f32));
|
||||
CHECK_FPE(f32, ffd, FS(f32) + FT(f32));
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -430,7 +428,7 @@ auto CPU::FADD_D(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f64, ffs + fft);
|
||||
CHECK_FPE(f64, ffd, ffs + fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -439,7 +437,7 @@ auto CPU::FCEIL_L_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundCeil<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundCeil<s64>(ffs));
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
||||
|
@ -447,7 +445,7 @@ auto CPU::FCEIL_L_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundCeil<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundCeil<s64>(ffs));
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
||||
|
@ -455,7 +453,7 @@ auto CPU::FCEIL_W_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundCeil<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundCeil<s32>(ffs));
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
||||
|
@ -463,7 +461,7 @@ auto CPU::FCEIL_W_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundCeil<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundCeil<s32>(ffs));
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
||||
|
@ -651,7 +649,7 @@ auto CPU::FCVT_S_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
auto ffd = CHECK_FPE(f32, (f32)ffs);
|
||||
CHECK_FPE(f32, ffd, (f32)ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -659,7 +657,7 @@ auto CPU::FCVT_S_D(u8 fd, u8 fs) -> void {
|
|||
auto CPU::FCVT_S_W(u8 fd, u8 fs) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(s32);
|
||||
auto ffd = CHECK_FPE(f32, ffs);
|
||||
CHECK_FPE(f32, ffd, ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -671,7 +669,7 @@ auto CPU::FCVT_S_L(u8 fd, u8 fs) -> void {
|
|||
if (fpeUnimplemented()) return exception.floatingPoint();
|
||||
return;
|
||||
}
|
||||
auto ffd = CHECK_FPE(f32, (f32)ffs);
|
||||
CHECK_FPE(f32, ffd, (f32)ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -680,7 +678,7 @@ auto CPU::FCVT_D_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
auto ffd = CHECK_FPE(f64, ffs);
|
||||
CHECK_FPE(f64, ffd, ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -693,7 +691,7 @@ auto CPU::FCVT_D_D(u8 fd, u8 fs) -> void {
|
|||
auto CPU::FCVT_D_W(u8 fd, u8 fs) -> void {
|
||||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(s32);
|
||||
auto ffd = CHECK_FPE(f64, (f64)ffs);
|
||||
CHECK_FPE(f64, ffd, (f64)ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -705,7 +703,7 @@ auto CPU::FCVT_D_L(u8 fd, u8 fs) -> void {
|
|||
if (fpeUnimplemented()) return exception.floatingPoint();
|
||||
return;
|
||||
}
|
||||
auto ffd = CHECK_FPE(f64, (f64)ffs);
|
||||
CHECK_FPE(f64, ffd, (f64)ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffs;
|
||||
}
|
||||
|
@ -714,7 +712,7 @@ auto CPU::FCVT_L_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundCurrent<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundCurrent<s64>(ffs));
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
||||
|
@ -722,7 +720,7 @@ auto CPU::FCVT_L_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundCurrent<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundCurrent<s64>(ffs));
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
||||
|
@ -730,7 +728,7 @@ auto CPU::FCVT_W_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundCurrent<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundCurrent<s32>(ffs));
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
||||
|
@ -738,7 +736,7 @@ auto CPU::FCVT_W_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundCurrent<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundCurrent<s32>(ffs));
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
||||
|
@ -747,7 +745,7 @@ auto CPU::FDIV_S(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f32, ffs / fft);
|
||||
CHECK_FPE(f32, ffd, ffs / fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -757,7 +755,7 @@ auto CPU::FDIV_D(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f64, ffs / fft);
|
||||
CHECK_FPE(f64, ffd, ffs / fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -766,7 +764,7 @@ auto CPU::FFLOOR_L_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundFloor<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundFloor<s64>(ffs));
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
||||
|
@ -774,7 +772,7 @@ auto CPU::FFLOOR_L_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundFloor<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundFloor<s64>(ffs));
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
||||
|
@ -782,7 +780,7 @@ auto CPU::FFLOOR_W_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundFloor<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundFloor<s32>(ffs));
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
||||
|
@ -790,7 +788,7 @@ auto CPU::FFLOOR_W_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundFloor<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundFloor<s32>(ffs));
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
||||
|
@ -809,7 +807,7 @@ auto CPU::FMUL_S(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f32, ffs * fft);
|
||||
CHECK_FPE(f32, ffd, ffs * fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -819,7 +817,7 @@ auto CPU::FMUL_D(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f64, ffs * fft);
|
||||
CHECK_FPE(f64, ffd, ffs * fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -828,7 +826,7 @@ auto CPU::FNEG_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
auto ffd = CHECK_FPE(f32, -ffs);
|
||||
CHECK_FPE(f32, ffd, -ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -837,7 +835,7 @@ auto CPU::FNEG_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
auto ffd = CHECK_FPE(f64, -ffs);
|
||||
CHECK_FPE(f64, ffd, -ffs);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -846,7 +844,7 @@ auto CPU::FROUND_L_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundNearest<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundNearest<s64>(ffs));
|
||||
if(ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
@ -855,7 +853,7 @@ auto CPU::FROUND_L_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundNearest<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundNearest<s64>(ffs));
|
||||
if(ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
@ -864,7 +862,7 @@ auto CPU::FROUND_W_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundNearest<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundNearest<s32>(ffs));
|
||||
if(ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
@ -873,7 +871,7 @@ auto CPU::FROUND_W_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundNearest<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundNearest<s32>(ffs));
|
||||
if(ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
@ -882,7 +880,7 @@ auto CPU::FSQRT_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
auto ffd = CHECK_FPE(f32, squareRoot(ffs));
|
||||
CHECK_FPE(f32, ffd, squareRoot(ffs));
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -891,7 +889,7 @@ auto CPU::FSQRT_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
auto ffd = CHECK_FPE(f64, squareRoot(ffs));
|
||||
CHECK_FPE(f64, ffd, squareRoot(ffs));
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -901,7 +899,7 @@ auto CPU::FSUB_S(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f32), fft = FT(f32);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f32, ffs - fft);
|
||||
CHECK_FPE(f32, ffd, ffs - fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f32) = ffd;
|
||||
}
|
||||
|
@ -911,7 +909,7 @@ auto CPU::FSUB_D(u8 fd, u8 fs, u8 ft) -> void {
|
|||
auto ffs = FS(f64), fft = FT(f64);
|
||||
if(!fpuCheckInput(ffs)) return;
|
||||
if(!fpuCheckInput(fft)) return;
|
||||
auto ffd = CHECK_FPE(f64, ffs - fft);
|
||||
CHECK_FPE(f64, ffd, ffs - fft);
|
||||
if(!fpuCheckOutput(ffd)) return;
|
||||
FD(f64) = ffd;
|
||||
}
|
||||
|
@ -920,7 +918,7 @@ auto CPU::FTRUNC_L_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundTrunc<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundTrunc<s64>(ffs));
|
||||
if((f32)ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
@ -929,7 +927,7 @@ auto CPU::FTRUNC_L_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s64>(ffs)) return;
|
||||
auto ffd = CHECK_FPE(s64, roundTrunc<s64>(ffs));
|
||||
CHECK_FPE(s64, ffd, roundTrunc<s64>(ffs));
|
||||
if((f64)ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s64) = ffd;
|
||||
}
|
||||
|
@ -938,7 +936,7 @@ auto CPU::FTRUNC_W_S(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f32);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundTrunc<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundTrunc<s32>(ffs));
|
||||
if((f32)ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
@ -947,7 +945,7 @@ auto CPU::FTRUNC_W_D(u8 fd, u8 fs) -> void {
|
|||
if(!fpuCheckStart()) return;
|
||||
auto ffs = FS(f64);
|
||||
if(!fpuCheckInputConv<s32>(ffs)) return;
|
||||
auto ffd = CHECK_FPE_CONV(s32, roundTrunc<s32>(ffs));
|
||||
CHECK_FPE_CONV(s32, ffd, roundTrunc<s32>(ffs));
|
||||
if((f64)ffd != ffs && fpeInexact()) return exception.floatingPoint();
|
||||
FD(s32) = ffd;
|
||||
}
|
||||
|
|
|
@ -252,9 +252,19 @@ auto CPU::DADDU(r64& rd, cr64& rs, cr64& rt) -> void {
|
|||
auto CPU::DDIV(cr64& rs, cr64& rt) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
if(rt.s64) {
|
||||
#if defined(_MSC_VER) || !defined(__SIZEOF_INT128__)
|
||||
if(rs.s64 != (-1LL << 63) || rt.s64 != -1LL) {
|
||||
LO.u64 = rs.s64 / rt.s64;
|
||||
HI.u64 = rs.s64 % rt.s64;
|
||||
} else {
|
||||
LO.u64 = rs.s64;
|
||||
HI.u64 = 0;
|
||||
}
|
||||
#else
|
||||
//cast to i128 to prevent exception on INT64_MIN / -1
|
||||
LO.u64 = s128(rs.s64) / s128(rt.s64);
|
||||
HI.u64 = s128(rs.s64) % s128(rt.s64);
|
||||
#endif
|
||||
} else {
|
||||
LO.u64 = rs.s64 < 0 ? +1 : -1;
|
||||
HI.u64 = rs.s64;
|
||||
|
@ -301,17 +311,41 @@ auto CPU::DIVU(cr64& rs, cr64& rt) -> void {
|
|||
|
||||
auto CPU::DMULT(cr64& rs, cr64& rt) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
u128 result = rs.s128() * rt.s128();
|
||||
#if defined(COMPILER_MICROSOFT) && (defined(ARCHITECTURE_AMD64) || defined(ARCHITECTURE_ARM64))
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
LO.s64 = _mul128(rs.s64, rt.s64, &HI.s64);
|
||||
#else
|
||||
LO.s64 = rs.s64 * rt.s64;
|
||||
HI.s64 = __mulh(rs.s64, rt.s64);
|
||||
#endif
|
||||
#else
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
u128 result = s128(rs.s64) * s128(rt.s64);
|
||||
#else
|
||||
u128 result = u128(rs.u64) * u128(rt.u64);
|
||||
if(rs.s64 < 0) result -= u128(rt.u64) << 64;
|
||||
if(rt.s64 < 0) result -= u128(rs.u64) << 64;
|
||||
#endif
|
||||
LO.u64 = result >> 0;
|
||||
HI.u64 = result >> 64;
|
||||
#endif
|
||||
step(8);
|
||||
}
|
||||
|
||||
auto CPU::DMULTU(cr64& rs, cr64& rt) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
u128 result = rs.u128() * rt.u128();
|
||||
#if defined(COMPILER_MICROSOFT) && (defined(ARCHITECTURE_AMD64) || defined(ARCHITECTURE_ARM64))
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
LO.u64 = _umul128(rs.u64, rt.u64, &HI.u64);
|
||||
#else
|
||||
LO.u64 = rs.u64 * rt.u64;
|
||||
HI.u64 = __umulh(rs.u64, rt.u64);
|
||||
#endif
|
||||
#else
|
||||
u128 result = u128(rs.u64) * u128(rt.u64);
|
||||
LO.u64 = result >> 0;
|
||||
HI.u64 = result >> 64;
|
||||
#endif
|
||||
step(8);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,21 +30,21 @@ auto CPU::kernelSegment64(u64 vaddr) const -> Context::Segment {
|
|||
if(vaddr <= 0x3fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x4000'00ff'ffff'ffffull) return Context::Segment::Mapped; //xksseg
|
||||
if(vaddr <= 0x7fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x8000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0x8000'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0x87ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x8800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0x8800'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0x8fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x9000'0000'ffff'ffffull) return Context::Segment::Direct; //xkphys*
|
||||
if(vaddr <= 0x9000'0000'ffff'ffffull) return Context::Segment::Direct32; //xkphys*
|
||||
if(vaddr <= 0x97ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0x9800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0x9800'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0x9fff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xa000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xa000'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0xa7ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xa800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xa800'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0xafff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xb000'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xb000'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0xb7ff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xb800'0000'ffff'ffffull) return Context::Segment::Cached; //xkphys*
|
||||
if(vaddr <= 0xb800'0000'ffff'ffffull) return Context::Segment::Cached32; //xkphys*
|
||||
if(vaddr <= 0xbfff'ffff'ffff'ffffull) return Context::Segment::Unused;
|
||||
if(vaddr <= 0xc000'00ff'7fff'ffffull) return Context::Segment::Mapped; //xkseg
|
||||
if(vaddr <= 0xffff'ffff'7fff'ffffull) return Context::Segment::Unused;
|
||||
|
@ -89,6 +89,7 @@ auto CPU::segment(u64 vaddr) -> Context::Segment {
|
|||
}
|
||||
|
||||
auto CPU::devirtualize(u64 vaddr) -> maybe<u64> {
|
||||
if(vaddrAlignedError<Word>(vaddr, false)) return nothing;
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
addressException(vaddr);
|
||||
|
@ -100,32 +101,55 @@ auto CPU::devirtualize(u64 vaddr) -> maybe<u64> {
|
|||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Direct:
|
||||
return vaddr & context.physMask;
|
||||
return vaddr & 0x1fff'ffff;
|
||||
case Context::Segment::Cached32:
|
||||
case Context::Segment::Direct32:
|
||||
return vaddr & 0xffff'ffff;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::fetch(u64 vaddr) -> u32 {
|
||||
template<u32 Size>
|
||||
inline auto CPU::busWrite(u32 address, u64 data) -> void {
|
||||
u32 cycles = 0;
|
||||
bus.write<Size>(address, data, cycles);
|
||||
step(cycles);
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
inline auto CPU::busRead(u32 address) -> u64 {
|
||||
u32 cycles = 0; u64 data;
|
||||
data = bus.read<Size>(address, cycles);
|
||||
return step(cycles), data;
|
||||
}
|
||||
|
||||
auto CPU::fetch(u64 vaddr) -> maybe<u32> {
|
||||
if(vaddrAlignedError<Word>(vaddr, false)) return nothing;
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return 0; //nop
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(vaddr)) {
|
||||
if(match.cache) return icache.fetch(match.address & context.physMask, cpu);
|
||||
step(1);
|
||||
return bus.read<Word>(match.address & context.physMask);
|
||||
return busRead<Word>(match.address & context.physMask);
|
||||
}
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
return 0; //nop
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
return icache.fetch(vaddr & context.physMask, cpu);
|
||||
return icache.fetch(vaddr & 0x1fff'ffff, cpu);
|
||||
case Context::Segment::Cached32:
|
||||
return icache.fetch(vaddr & 0xffff'ffff, cpu);
|
||||
case Context::Segment::Direct:
|
||||
step(1);
|
||||
return bus.read<Word>(vaddr & context.physMask);
|
||||
return busRead<Word>(vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Direct32:
|
||||
step(1);
|
||||
return busRead<Word>(vaddr & 0xffff'ffff);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
|
@ -133,21 +157,7 @@ auto CPU::fetch(u64 vaddr) -> u32 {
|
|||
|
||||
template<u32 Size>
|
||||
auto CPU::read(u64 vaddr) -> maybe<u64> {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(vaddr & Size - 1)) {
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
}
|
||||
if (context.bits == 32 && unlikely((s32)vaddr != vaddr)) {
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
}
|
||||
}
|
||||
|
||||
if(vaddrAlignedError<Size>(vaddr, false)) return nothing;
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1);
|
||||
|
@ -158,16 +168,21 @@ auto CPU::read(u64 vaddr) -> maybe<u64> {
|
|||
if(auto match = tlb.load(vaddr)) {
|
||||
if(match.cache) return dcache.read<Size>(match.address & context.physMask);
|
||||
step(1);
|
||||
return bus.read<Size>(match.address & context.physMask);
|
||||
return busRead<Size>(match.address & context.physMask);
|
||||
}
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.read<Size>(vaddr & context.physMask);
|
||||
return dcache.read<Size>(vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Cached32:
|
||||
return dcache.read<Size>(vaddr & 0xffff'ffff);
|
||||
case Context::Segment::Direct:
|
||||
step(1);
|
||||
return bus.read<Size>(vaddr & context.physMask);
|
||||
return busRead<Size>(vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Direct32:
|
||||
step(1);
|
||||
return busRead<Size>(vaddr & 0xffff'ffff);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
|
@ -175,21 +190,7 @@ auto CPU::read(u64 vaddr) -> maybe<u64> {
|
|||
|
||||
template<u32 Size>
|
||||
auto CPU::write(u64 vaddr, u64 data) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(vaddr & Size - 1)) {
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
exception.addressStore();
|
||||
return false;
|
||||
}
|
||||
if (context.bits == 32 && unlikely((s32)vaddr != vaddr)) {
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
exception.addressStore();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(vaddrAlignedError<Size>(vaddr, true)) return false;
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1);
|
||||
|
@ -200,21 +201,47 @@ auto CPU::write(u64 vaddr, u64 data) -> bool {
|
|||
if(auto match = tlb.store(vaddr)) {
|
||||
if(match.cache) return dcache.write<Size>(match.address & context.physMask, data), true;
|
||||
step(1);
|
||||
return bus.write<Size>(match.address & context.physMask, data), true;
|
||||
return busWrite<Size>(match.address & context.physMask, data), true;
|
||||
}
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
return false;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.write<Size>(vaddr & context.physMask, data), true;
|
||||
return dcache.write<Size>(vaddr & 0x1fff'ffff, data), true;
|
||||
case Context::Segment::Cached32:
|
||||
return dcache.write<Size>(vaddr & 0xffff'ffff, data), true;
|
||||
case Context::Segment::Direct:
|
||||
step(1);
|
||||
return bus.write<Size>(vaddr & context.physMask, data), true;
|
||||
return busWrite<Size>(vaddr & 0x1fff'ffff, data), true;
|
||||
case Context::Segment::Direct32:
|
||||
step(1);
|
||||
return busWrite<Size>(vaddr & 0xffff'ffff, data), true;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::vaddrAlignedError(u64 vaddr, bool write) -> bool {
|
||||
if constexpr(Accuracy::CPU::AddressErrors) {
|
||||
if(unlikely(vaddr & Size - 1)) {
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
if(write) exception.addressStore();
|
||||
else exception.addressLoad();
|
||||
return true;
|
||||
}
|
||||
if (context.bits == 32 && unlikely((s32)vaddr != vaddr)) {
|
||||
step(1);
|
||||
addressException(vaddr);
|
||||
if(write) exception.addressStore();
|
||||
else exception.addressLoad();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CPU::addressException(u64 vaddr) -> void {
|
||||
scc.badVirtualAddress = vaddr;
|
||||
scc.tlb.virtualAddress.bit(13,39) = vaddr >> 13;
|
||||
|
|
|
@ -4,17 +4,15 @@ auto CPU::Recompiler::pool(u32 address) -> Pool* {
|
|||
return pool;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::block(u32 address) -> Block& {
|
||||
auto pool = this->pool(address);
|
||||
auto& block = pool->blocks[address >> 2 & 0x3f];
|
||||
if(block.code) return block;
|
||||
emit(address, block);
|
||||
pool->dirty = true;
|
||||
auto CPU::Recompiler::block(u32 address) -> Block* {
|
||||
if(auto block = pool(address)->blocks[address >> 2 & 0x3f]) return block;
|
||||
auto block = emit(address);
|
||||
pool(address)->blocks[address >> 2 & 0x3f] = block;
|
||||
memory::jitprotect(true);
|
||||
return block;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emit(u32 address, Block& block) -> void {
|
||||
auto CPU::Recompiler::emit(u32 address) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("CPU allocator flush\n");
|
||||
memory::jitprotect(false);
|
||||
|
@ -23,11 +21,13 @@ auto CPU::Recompiler::emit(u32 address, Block& block) -> void {
|
|||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
beginFunction(3);
|
||||
|
||||
u32 memCycles;
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u32 instruction = bus.read<Word>(address);
|
||||
u32 instruction = bus.read<Word>(address, memCycles);
|
||||
bool branched = emitEXECUTE(instruction);
|
||||
if(unlikely(instruction == 0x1000'ffff)) {
|
||||
//accelerate idle loops
|
||||
|
@ -43,9 +43,10 @@ auto CPU::Recompiler::emit(u32 address, Block& block) -> void {
|
|||
jumpEpilog();
|
||||
|
||||
memory::jitprotect(false);
|
||||
block.code = endFunction();
|
||||
block->code = endFunction();
|
||||
|
||||
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
|
||||
return block;
|
||||
}
|
||||
|
||||
#define Sa (instruction >> 6 & 31)
|
||||
|
@ -284,7 +285,7 @@ auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x1c ... 0x1f: {
|
||||
case range4(0x1c, 0x1f): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -599,9 +600,7 @@ auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
|
||||
//SLLV Rd,Rt,Rs
|
||||
case 0x04: {
|
||||
mov32(reg(0), mem(Rt32));
|
||||
and32(reg(1), mem(Rs32), imm(31));
|
||||
shl32(reg(0), reg(0), reg(1));
|
||||
mshl32(reg(0), mem(Rt32), mem(Rs32));
|
||||
mov64_s32(reg(0), reg(0));
|
||||
mov64(mem(Rd), reg(0));
|
||||
return 0;
|
||||
|
@ -615,9 +614,7 @@ auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
|
||||
//SRLV Rd,Rt,RS
|
||||
case 0x06: {
|
||||
mov32(reg(0), mem(Rt32));
|
||||
and32(reg(1), mem(Rs32), imm(31));
|
||||
lshr32(reg(0), reg(0), reg(1));
|
||||
mlshr32(reg(0), mem(Rt32), mem(Rs32));
|
||||
mov64_s32(reg(0), reg(0));
|
||||
mov64(mem(Rd), reg(0));
|
||||
return 0;
|
||||
|
@ -625,7 +622,7 @@ auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
|
||||
//SRAV Rd,Rt,Rs
|
||||
case 0x07: {
|
||||
and32(reg(1), mem(Rs32), imm(31));
|
||||
and64(reg(1), mem(Rs), imm(31));
|
||||
ashr64(reg(0), mem(Rt), reg(1));
|
||||
mov64_s32(reg(0), reg(0));
|
||||
mov64(mem(Rd), reg(0));
|
||||
|
@ -648,7 +645,7 @@ auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x0a ... 0x0b: {
|
||||
case range2(0x0a, 0x0b): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -853,13 +850,13 @@ auto CPU::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
//NOR Rd,Rs,Rt
|
||||
case 0x27: {
|
||||
or64(reg(0), mem(Rs), mem(Rt));
|
||||
not64(reg(0), reg(0));
|
||||
xor64(reg(0), reg(0), imm(-1));
|
||||
mov64(mem(Rd), reg(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
case 0x28 ... 0x29: {
|
||||
case range2(0x28, 0x29): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1081,7 +1078,7 @@ auto CPU::Recompiler::emitREGIMM(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x04 ... 0x07: {
|
||||
case range4(0x04, 0x07): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1179,7 +1176,7 @@ auto CPU::Recompiler::emitREGIMM(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x14 ... 0x1f: {
|
||||
case range12(0x14, 0x1f): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1209,7 +1206,7 @@ auto CPU::Recompiler::emitSCC(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x02 ... 0x03: {
|
||||
case range2(0x02, 0x03): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1231,7 +1228,7 @@ auto CPU::Recompiler::emitSCC(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x06 ... 0x0f: {
|
||||
case range10(0x06, 0x0f): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1348,7 +1345,7 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x09 ... 0x0f: {
|
||||
case range7(0x09, 0x0f): {
|
||||
call(&CPU::INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1951,12 +1948,12 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
|
||||
if((instruction >> 21 & 31) == 20)
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x08 ... 0x0f: {
|
||||
case range8(0x08, 0x0f): {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case 0x24 ... 0x25: {
|
||||
case range2(0x24, 0x25): {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1981,11 +1978,11 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
|
||||
if((instruction >> 21 & 31) == 21)
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x08 ... 0x0f: {
|
||||
case range8(0x08, 0x0f): {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
case 0x24 ... 0x25: {
|
||||
case range2(0x24, 0x25): {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
@ -2069,7 +2066,7 @@ auto CPU::Recompiler::emitCOP2(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x07 ... 0x0f: {
|
||||
case range9(0x07, 0x0f): {
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s(scc.tagLo.physicalAddress);
|
||||
s(scc.epcError);
|
||||
s(scc.latch);
|
||||
s(scc.nmiPending);
|
||||
|
||||
for(auto& r : fpu.r) s(r.u64);
|
||||
s(fpu.csr.roundMode);
|
||||
|
|
|
@ -93,10 +93,12 @@ auto DD::disconnect() -> void {
|
|||
}
|
||||
|
||||
auto DD::save() -> void {
|
||||
/*if(disk)
|
||||
#if false
|
||||
if(disk)
|
||||
if(auto fp = pak->write("program.disk")) {
|
||||
disk.save(fp);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
rtcSave();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//Disk Drive
|
||||
|
||||
struct DD : Memory::IO<DD> {
|
||||
#include <nall/bcd.hpp>
|
||||
|
||||
struct DD : Memory::PI<DD> {
|
||||
Node::Object obj;
|
||||
Node::Port port;
|
||||
Node::Peripheral node;
|
||||
|
@ -65,6 +67,8 @@ struct DD : Memory::IO<DD> {
|
|||
auto rtcTickSecond() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readHalf(u32 address) -> u16;
|
||||
auto writeHalf(u32 address, u16 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
|
@ -76,13 +80,6 @@ struct DD : Memory::IO<DD> {
|
|||
string cic;
|
||||
} information;
|
||||
|
||||
struct BCD {
|
||||
static auto encode(u8 value) -> u8 { return value / 10 << 4 | value % 10; }
|
||||
static auto decode(u8 value) -> u8 { return (value >> 4) * 10 + (value & 15); }
|
||||
};
|
||||
|
||||
std::function<u64()> rtcCallback = []() { return 0; };
|
||||
|
||||
private:
|
||||
struct Interrupt {
|
||||
b1 line = 0;
|
||||
|
|
|
@ -20,7 +20,7 @@ auto DD::seekSector(n8 sector) -> u32 {
|
|||
n1 headCalc = io.currentTrack.bit(12);
|
||||
|
||||
u32 startOffsetTable[16] = {0x0,0x5F15E0,0xB79D00,0x10801A0,0x1523720,0x1963D80,0x1D414C0,0x20BBCE0,
|
||||
0x23196E0,0x28A1E00,0x2DF5DC0,0x3299340,0x36D99A0,0x3AB70E0,0x3E31900,0x4149200};
|
||||
0x23196E0,0x28A1E00,0x2DF5DC0,0x3299340,0x36D99A0,0x3AB70E0,0x3E31900,0x4149200};
|
||||
|
||||
u16 trackPhysicalTable[] = {0x000, 0x09E, 0x13C, 0x1D1, 0x266, 0x2FB, 0x390, 0x425};
|
||||
u16 blockSizeTable[] = {0x4D08, 0x47B8, 0x4510, 0x3FC0, 0x3A70, 0x3520, 0x2FD0, 0x2A80,
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
auto DD::readWord(u32 address) -> u32 {
|
||||
address = (address & 0x7f) >> 2;
|
||||
n32 data;
|
||||
auto DD::readHalf(u32 address) -> u16 {
|
||||
address = (address & 0x7f) >> 1;
|
||||
n16 data = 0;
|
||||
|
||||
//ASIC_DATA
|
||||
if(address == 0) {
|
||||
data.bit(16,31) = io.data;
|
||||
data.bit(0,15) = io.data;
|
||||
}
|
||||
|
||||
//ASIC_MISC_REG
|
||||
if(address == 1) {
|
||||
if(address == 2) {
|
||||
}
|
||||
|
||||
//ASIC_STATUS
|
||||
if(address == 2) {
|
||||
data.bit(16) = io.status.diskChanged;
|
||||
data.bit(17) = io.status.mechaError;
|
||||
data.bit(18) = io.status.writeProtect;
|
||||
data.bit(19) = io.status.headRetracted;
|
||||
data.bit(20) = io.status.spindleMotorStopped;
|
||||
data.bit(22) = io.status.resetState;
|
||||
data.bit(23) = io.status.busyState;
|
||||
data.bit(24) = (bool)disk; //disk present
|
||||
data.bit(25) = irq.mecha.line;
|
||||
data.bit(26) = irq.bm.line;
|
||||
data.bit(27) = io.bm.error;
|
||||
data.bit(28) = io.status.requestC2Sector;
|
||||
data.bit(30) = io.status.requestUserSector;
|
||||
if(address == 4) {
|
||||
data.bit(0) = io.status.diskChanged;
|
||||
data.bit(1) = io.status.mechaError;
|
||||
data.bit(2) = io.status.writeProtect;
|
||||
data.bit(3) = io.status.headRetracted;
|
||||
data.bit(4) = io.status.spindleMotorStopped;
|
||||
data.bit(6) = io.status.resetState;
|
||||
data.bit(7) = io.status.busyState;
|
||||
data.bit(8) = (bool)disk; //disk present
|
||||
data.bit(9) = irq.mecha.line;
|
||||
data.bit(10) = irq.bm.line;
|
||||
data.bit(11) = io.bm.error;
|
||||
data.bit(12) = io.status.requestC2Sector;
|
||||
data.bit(14) = io.status.requestUserSector;
|
||||
|
||||
//acknowledge bm interrupt (tested on real hardware)
|
||||
if(irq.bm.line) {
|
||||
|
@ -36,133 +36,133 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
}
|
||||
|
||||
//ASIC_CUR_TK
|
||||
if(address == 3) {
|
||||
data.bit(16,31) = io.currentTrack;
|
||||
if(address == 6) {
|
||||
data.bit(0,15) = io.currentTrack;
|
||||
}
|
||||
|
||||
//ASIC_BM_STATUS
|
||||
if(address == 4) {
|
||||
data.bit(16) = io.bm.c1Error;
|
||||
data.bit(21) = io.bm.c1Single;
|
||||
data.bit(22) = io.bm.c1Double;
|
||||
data.bit(23) = io.bm.c1Correct;
|
||||
data.bit(24) = io.bm.blockTransfer;
|
||||
data.bit(25) = io.micro.error;
|
||||
data.bit(26) = io.bm.error;
|
||||
data.bit(31) = io.bm.start;
|
||||
if(address == 8) {
|
||||
data.bit(0) = io.bm.c1Error;
|
||||
data.bit(5) = io.bm.c1Single;
|
||||
data.bit(6) = io.bm.c1Double;
|
||||
data.bit(7) = io.bm.c1Correct;
|
||||
data.bit(8) = io.bm.blockTransfer;
|
||||
data.bit(9) = io.micro.error;
|
||||
data.bit(10) = io.bm.error;
|
||||
data.bit(15) = io.bm.start;
|
||||
}
|
||||
|
||||
//ASIC_ERR_SECTOR
|
||||
if(address == 5) {
|
||||
data.bit(16,23) = io.error.sector;
|
||||
data.bit(24) = io.error.selfStop;
|
||||
data.bit(25) = io.error.clockUnlock;
|
||||
data.bit(26) = ~(bool)disk; //no disk
|
||||
data.bit(27) = io.error.offTrack;
|
||||
data.bit(28) = io.error.overrun;
|
||||
data.bit(29) = io.error.spindle;
|
||||
data.bit(30) = io.micro.error;
|
||||
data.bit(31) = io.error.am;
|
||||
if(address == 10) {
|
||||
data.bit(0,7) = io.error.sector;
|
||||
data.bit(8) = io.error.selfStop;
|
||||
data.bit(9) = io.error.clockUnlock;
|
||||
data.bit(10) = ~(bool)disk; //no disk
|
||||
data.bit(11) = io.error.offTrack;
|
||||
data.bit(12) = io.error.overrun;
|
||||
data.bit(13) = io.error.spindle;
|
||||
data.bit(14) = io.micro.error;
|
||||
data.bit(15) = io.error.am;
|
||||
}
|
||||
|
||||
//ASIC_SEQ_STATUS
|
||||
if(address == 6) {
|
||||
if(address == 12) {
|
||||
}
|
||||
|
||||
//ASIC_CUR_SECTOR
|
||||
if(address == 7) {
|
||||
data.bit(24,31) = io.currentSector;
|
||||
data.bit(16,23) = 0xc3;
|
||||
if(address == 14) {
|
||||
data.bit(8,15) = io.currentSector;
|
||||
data.bit(0,7) = 0xc3;
|
||||
}
|
||||
|
||||
//ASIC_HARD_RESET
|
||||
if(address == 8) {
|
||||
if(address == 16) {
|
||||
}
|
||||
|
||||
//ASIC_C1_S0
|
||||
if(address == 9) {
|
||||
if(address == 18) {
|
||||
}
|
||||
|
||||
//ASIC_HOST_SECBYTE
|
||||
if(address == 10) {
|
||||
data.bit(16,23) = io.sectorSizeBuf;
|
||||
if(address == 20) {
|
||||
data.bit(0,7) = io.sectorSizeBuf;
|
||||
}
|
||||
|
||||
//ASIC_C1_S2
|
||||
if(address == 11) {
|
||||
if(address == 22) {
|
||||
}
|
||||
|
||||
//ASIC_SEC_BYTE
|
||||
if(address == 12) {
|
||||
data.bit(16,23) = io.sectorSize;
|
||||
data.bit(24,31) = io.sectorBlock;
|
||||
if(address == 24) {
|
||||
data.bit(0,7) = io.sectorSize;
|
||||
data.bit(8,15) = io.sectorBlock;
|
||||
}
|
||||
|
||||
//ASIC_C1_S4
|
||||
if(address == 13) {
|
||||
if(address == 26) {
|
||||
}
|
||||
|
||||
//ASIC_C1_S6
|
||||
if(address == 14) {
|
||||
if(address == 28) {
|
||||
}
|
||||
|
||||
//ASIC_CUR_ADDRESS
|
||||
if(address == 15) {
|
||||
if(address == 30) {
|
||||
}
|
||||
|
||||
//ASIC_ID_REG
|
||||
if(address == 16) {
|
||||
data.bit(16,31) = io.id;
|
||||
if(address == 32) {
|
||||
data.bit(0,15) = io.id;
|
||||
}
|
||||
|
||||
//ASIC_TEST_REG
|
||||
if(address == 17) {
|
||||
if(address == 34) {
|
||||
}
|
||||
|
||||
//ASIC_TEST_PIN_SEL
|
||||
if(address == 18) {
|
||||
if(address == 36) {
|
||||
}
|
||||
|
||||
debugger.io(Read, address, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto DD::writeWord(u32 address, u32 data_) -> void {
|
||||
address = (address & 0x7f) >> 2;
|
||||
n32 data = data_;
|
||||
auto DD::writeHalf(u32 address, u16 data_) -> void {
|
||||
address = (address & 0x7f) >> 1;
|
||||
n16 data = data_;
|
||||
|
||||
//ASIC_DATA
|
||||
if(address == 0) {
|
||||
io.data = data.bit(16,31);
|
||||
io.data = data.bit(0,15);
|
||||
}
|
||||
|
||||
//ASIC_MISC_REG
|
||||
if(address == 1) {
|
||||
if(address == 2) {
|
||||
}
|
||||
|
||||
//ASIC_CMD
|
||||
if(address == 2) {
|
||||
command(data.bit(16,31));
|
||||
if(address == 4) {
|
||||
command(data.bit(0,15));
|
||||
}
|
||||
|
||||
//ASIC_CUR_TK
|
||||
if(address == 3) {
|
||||
if(address == 6) {
|
||||
}
|
||||
|
||||
//ASIC_BM_CTL
|
||||
if(address == 4) {
|
||||
io.bm.reset |= data.bit(28);
|
||||
io.bm.readMode = data.bit(30);
|
||||
//irq.bm.mask = ~data.bit(29);
|
||||
io.bm.disableORcheck = data.bit(27);
|
||||
io.bm.disableC1Correction = data.bit(26);
|
||||
io.bm.blockTransfer = data.bit(25);
|
||||
if (data.bit(24)) {
|
||||
if(address == 8) {
|
||||
io.bm.reset |= data.bit(12);
|
||||
io.bm.readMode = data.bit(14);
|
||||
//irq.bm.mask = ~data.bit(13);
|
||||
io.bm.disableORcheck = data.bit(11);
|
||||
io.bm.disableC1Correction = data.bit(10);
|
||||
io.bm.blockTransfer = data.bit(9);
|
||||
if (data.bit(8)) {
|
||||
//mecha int reset
|
||||
lower(IRQ::MECHA);
|
||||
}
|
||||
io.currentSector = data.bit(16,23);
|
||||
if (!data.bit(28) && io.bm.reset) {
|
||||
io.currentSector = data.bit(0,7);
|
||||
if (!data.bit(12) && io.bm.reset) {
|
||||
//BM reset
|
||||
io.bm.start = 0;
|
||||
io.bm.error = 0;
|
||||
|
@ -172,76 +172,88 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
lower(IRQ::BM);
|
||||
}
|
||||
|
||||
if(data.bit(31) && disk) {
|
||||
if(data.bit(15) && disk) {
|
||||
//start BM
|
||||
io.bm.start |= data.bit(31);
|
||||
io.bm.start |= data.bit(15);
|
||||
//TODO: proper research into seek and access times
|
||||
queue.insert(Queue::DD_BM_Request, 50'000 + (io.currentTrack.bit(0,11) / 15));
|
||||
}
|
||||
}
|
||||
|
||||
//ASIC_ERR_SECTOR
|
||||
if(address == 5) {
|
||||
if(address == 10) {
|
||||
}
|
||||
|
||||
//ASIC_SEQ_CTL
|
||||
if(address == 6) {
|
||||
io.micro.enable = data.bit(30);
|
||||
if(address == 12) {
|
||||
io.micro.enable = data.bit(14);
|
||||
}
|
||||
|
||||
//ASIC_CUR_SECTOR
|
||||
if(address == 7) {
|
||||
if(address == 14) {
|
||||
}
|
||||
|
||||
//ASIC_HARD_RESET
|
||||
if(address == 8) {
|
||||
if(address == 16) {
|
||||
if((data >> 16) == 0xAAAA) {
|
||||
power(true);
|
||||
}
|
||||
}
|
||||
|
||||
//ASIC_C1_S0
|
||||
if(address == 9) {
|
||||
if(address == 18) {
|
||||
}
|
||||
|
||||
//ASIC_HOST_SECBYTE
|
||||
if(address == 10) {
|
||||
io.sectorSizeBuf = data.bit(16,23);
|
||||
if(address == 20) {
|
||||
io.sectorSizeBuf = data.bit(0,7);
|
||||
}
|
||||
|
||||
//ASIC_C1_S2
|
||||
if(address == 11) {
|
||||
io.sectorSize = data.bit(16,23);
|
||||
io.sectorBlock = data.bit(24,31);
|
||||
if(address == 22) {
|
||||
io.sectorSize = data.bit(0,7);
|
||||
io.sectorBlock = data.bit(8,15);
|
||||
}
|
||||
|
||||
//ASIC_SEC_BYTE
|
||||
if(address == 12) {
|
||||
if(address == 24) {
|
||||
}
|
||||
|
||||
//ASIC_C1_S4
|
||||
if(address == 13) {
|
||||
if(address == 26) {
|
||||
}
|
||||
|
||||
//ASIC_C1_S6
|
||||
if(address == 14) {
|
||||
if(address == 28) {
|
||||
}
|
||||
|
||||
//ASIC_CUR_ADDRESS
|
||||
if(address == 15) {
|
||||
if(address == 30) {
|
||||
}
|
||||
|
||||
//ASIC_ID_REG
|
||||
if(address == 16) {
|
||||
if(address == 32) {
|
||||
}
|
||||
|
||||
//ASIC_TEST_REG
|
||||
if(address == 17) {
|
||||
if(address == 34) {
|
||||
}
|
||||
|
||||
//ASIC_TEST_PIN_SEL
|
||||
if(address == 18) {
|
||||
if(address == 36) {
|
||||
}
|
||||
|
||||
debugger.io(Write, address, data);
|
||||
}
|
||||
|
||||
auto DD::readWord(u32 address) -> u32 {
|
||||
n32 data;
|
||||
data.bit(16,31) = readHalf(address + 0);
|
||||
data.bit( 0,15) = readHalf(address + 2);
|
||||
return (u32)data;
|
||||
}
|
||||
|
||||
auto DD::writeWord(u32 address, u32 data) -> void {
|
||||
writeHalf(address + 0, data >> 16);
|
||||
writeHalf(address + 2, data & 0xffff);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
auto DD::rtcLoad() -> void {
|
||||
/*if(auto fp = system.pak->read("time.rtc")) {
|
||||
#if false
|
||||
if(auto fp = system.pak->read("time.rtc")) {
|
||||
rtc.load(fp);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
n64 check = 0;
|
||||
for(auto n : range(8)) check.byte(n) = rtc.read<Byte>(n);
|
||||
|
@ -11,17 +13,19 @@ auto DD::rtcLoad() -> void {
|
|||
for(auto n : range(8)) timestamp.byte(n) = rtc.read<Byte>(8 + n);
|
||||
if(!~timestamp) return; //new save file
|
||||
|
||||
timestamp = rtcCallback() - timestamp;
|
||||
timestamp = platform->time() - timestamp;
|
||||
while(timestamp--) rtcTickSecond();
|
||||
}
|
||||
|
||||
auto DD::rtcSave() -> void {
|
||||
n64 timestamp = rtcCallback();
|
||||
n64 timestamp = platform->time();
|
||||
for(auto n : range(8)) rtc.write<Byte>(8 + n, timestamp.byte(n));
|
||||
|
||||
/*if(auto fp = system.pak->write("time.rtc")) {
|
||||
#if false
|
||||
if(auto fp = system.pak->write("time.rtc")) {
|
||||
rtc.save(fp);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
auto DD::rtcTick(u32 offset) -> void {
|
||||
|
|
|
@ -1,66 +1,66 @@
|
|||
auto DD::serialize(serializer& s) -> void {
|
||||
s(irq.bm.line);
|
||||
s(irq.bm.mask);
|
||||
s(irq.mecha.line);
|
||||
s(irq.mecha.mask);
|
||||
s(irq.bm.line);
|
||||
s(irq.bm.mask);
|
||||
s(irq.mecha.line);
|
||||
s(irq.mecha.mask);
|
||||
|
||||
s(ctl.diskType);
|
||||
s(ctl.error.selfDiagnostic);
|
||||
s(ctl.error.servoNG);
|
||||
s(ctl.error.indexGapNG);
|
||||
s(ctl.error.timeout);
|
||||
s(ctl.error.undefinedCommand);
|
||||
s(ctl.error.invalidParam);
|
||||
s(ctl.error.unknown);
|
||||
s(ctl.standbyDelayDisable);
|
||||
s(ctl.standbyDelay);
|
||||
s(ctl.sleepDelayDisable);
|
||||
s(ctl.sleepDelay);
|
||||
s(ctl.ledOnTime);
|
||||
s(ctl.ledOffTime);
|
||||
s(ctl.diskType);
|
||||
s(ctl.error.selfDiagnostic);
|
||||
s(ctl.error.servoNG);
|
||||
s(ctl.error.indexGapNG);
|
||||
s(ctl.error.timeout);
|
||||
s(ctl.error.undefinedCommand);
|
||||
s(ctl.error.invalidParam);
|
||||
s(ctl.error.unknown);
|
||||
s(ctl.standbyDelayDisable);
|
||||
s(ctl.standbyDelay);
|
||||
s(ctl.sleepDelayDisable);
|
||||
s(ctl.sleepDelay);
|
||||
s(ctl.ledOnTime);
|
||||
s(ctl.ledOffTime);
|
||||
|
||||
s(io.data);
|
||||
s(io.data);
|
||||
|
||||
s(io.status.requestUserSector);
|
||||
s(io.status.requestC2Sector);
|
||||
s(io.status.busyState);
|
||||
s(io.status.resetState);
|
||||
s(io.status.spindleMotorStopped);
|
||||
s(io.status.headRetracted);
|
||||
s(io.status.writeProtect);
|
||||
s(io.status.mechaError);
|
||||
s(io.status.diskChanged);
|
||||
s(io.status.requestUserSector);
|
||||
s(io.status.requestC2Sector);
|
||||
s(io.status.busyState);
|
||||
s(io.status.resetState);
|
||||
s(io.status.spindleMotorStopped);
|
||||
s(io.status.headRetracted);
|
||||
s(io.status.writeProtect);
|
||||
s(io.status.mechaError);
|
||||
s(io.status.diskChanged);
|
||||
|
||||
s(io.currentTrack);
|
||||
s(io.currentSector);
|
||||
s(io.currentTrack);
|
||||
s(io.currentSector);
|
||||
|
||||
s(io.sectorSizeBuf);
|
||||
s(io.sectorSize);
|
||||
s(io.sectorBlock);
|
||||
s(io.id);
|
||||
s(io.sectorSizeBuf);
|
||||
s(io.sectorSize);
|
||||
s(io.sectorBlock);
|
||||
s(io.id);
|
||||
|
||||
s(io.bm.start);
|
||||
s(io.bm.reset);
|
||||
s(io.bm.error);
|
||||
s(io.bm.blockTransfer);
|
||||
s(io.bm.c1Correct);
|
||||
s(io.bm.c1Double);
|
||||
s(io.bm.c1Single);
|
||||
s(io.bm.c1Error);
|
||||
s(io.bm.readMode);
|
||||
s(io.bm.disableORcheck);
|
||||
s(io.bm.disableC1Correction);
|
||||
s(io.bm.start);
|
||||
s(io.bm.reset);
|
||||
s(io.bm.error);
|
||||
s(io.bm.blockTransfer);
|
||||
s(io.bm.c1Correct);
|
||||
s(io.bm.c1Double);
|
||||
s(io.bm.c1Single);
|
||||
s(io.bm.c1Error);
|
||||
s(io.bm.readMode);
|
||||
s(io.bm.disableORcheck);
|
||||
s(io.bm.disableC1Correction);
|
||||
|
||||
s(io.error.am);
|
||||
s(io.error.spindle);
|
||||
s(io.error.overrun);
|
||||
s(io.error.offTrack);
|
||||
s(io.error.clockUnlock);
|
||||
s(io.error.selfStop);
|
||||
s(io.error.sector);
|
||||
s(io.error.am);
|
||||
s(io.error.spindle);
|
||||
s(io.error.overrun);
|
||||
s(io.error.offTrack);
|
||||
s(io.error.clockUnlock);
|
||||
s(io.error.selfStop);
|
||||
s(io.error.sector);
|
||||
|
||||
s(io.micro.enable);
|
||||
s(io.micro.error);
|
||||
s(io.micro.enable);
|
||||
s(io.micro.error);
|
||||
|
||||
s(state.seek);
|
||||
s(state.seek);
|
||||
}
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
template<u32 Size>
|
||||
inline auto Bus::read(u32 address) -> u64 {
|
||||
inline auto Bus::read(u32 address, u32& cycles) -> u64 {
|
||||
static constexpr u64 unmapped = 0;
|
||||
address &= 0x1fff'ffff - (Size - 1);
|
||||
|
||||
if(address <= 0x007f'ffff) return rdram.ram.read<Size>(address);
|
||||
if(address <= 0x03ef'ffff) return unmapped;
|
||||
if(address <= 0x03ff'ffff) return rdram.read<Size>(address);
|
||||
if(address <= 0x0407'ffff) return rsp.read<Size>(address);
|
||||
if(address <= 0x040f'ffff) return rsp.status.read<Size>(address);
|
||||
if(address <= 0x041f'ffff) return rdp.read<Size>(address);
|
||||
if(address <= 0x042f'ffff) return rdp.io.read<Size>(address);
|
||||
if(address <= 0x043f'ffff) return mi.read<Size>(address);
|
||||
if(address <= 0x044f'ffff) return vi.read<Size>(address);
|
||||
if(address <= 0x045f'ffff) return ai.read<Size>(address);
|
||||
if(address <= 0x046f'ffff) return pi.read<Size>(address);
|
||||
if(address <= 0x047f'ffff) return ri.read<Size>(address);
|
||||
if(address <= 0x048f'ffff) return si.read<Size>(address);
|
||||
if(address <= 0x03ff'ffff) return rdram.read<Size>(address, cycles);
|
||||
if(address <= 0x0407'ffff) return rsp.read<Size>(address, cycles);
|
||||
if(address <= 0x040f'ffff) return rsp.status.read<Size>(address, cycles);
|
||||
if(address <= 0x041f'ffff) return rdp.read<Size>(address, cycles);
|
||||
if(address <= 0x042f'ffff) return rdp.io.read<Size>(address, cycles);
|
||||
if(address <= 0x043f'ffff) return mi.read<Size>(address, cycles);
|
||||
if(address <= 0x044f'ffff) return vi.read<Size>(address, cycles);
|
||||
if(address <= 0x045f'ffff) return ai.read<Size>(address, cycles);
|
||||
if(address <= 0x046f'ffff) return pi.read<Size>(address, cycles);
|
||||
if(address <= 0x047f'ffff) return ri.read<Size>(address, cycles);
|
||||
if(address <= 0x048f'ffff) return si.read<Size>(address, cycles);
|
||||
if(address <= 0x04ff'ffff) return unmapped;
|
||||
if(address <= 0x1fbf'ffff) return pi.read<Size>(address);
|
||||
if(address <= 0x1fcf'ffff) return pif.read<Size>(address);
|
||||
if(address <= 0x7fff'ffff) return pi.read<Size>(address);
|
||||
if(address <= 0x1fbf'ffff) return pi.read<Size>(address, cycles);
|
||||
if(address <= 0x1fcf'ffff) return si.read<Size>(address, cycles);
|
||||
if(address <= 0x7fff'ffff) return pi.read<Size>(address, cycles);
|
||||
return unmapped;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
inline auto Bus::write(u32 address, u64 data) -> void {
|
||||
inline auto Bus::write(u32 address, u64 data, u32& cycles) -> void {
|
||||
address &= 0x1fff'ffff - (Size - 1);
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
cpu.recompiler.invalidate(address + 0); if constexpr(Size == Dual)
|
||||
|
@ -33,20 +33,20 @@ inline auto Bus::write(u32 address, u64 data) -> void {
|
|||
|
||||
if(address <= 0x007f'ffff) return rdram.ram.write<Size>(address, data);
|
||||
if(address <= 0x03ef'ffff) return;
|
||||
if(address <= 0x03ff'ffff) return rdram.write<Size>(address, data);
|
||||
if(address <= 0x0407'ffff) return rsp.write<Size>(address, data);
|
||||
if(address <= 0x040f'ffff) return rsp.status.write<Size>(address, data);
|
||||
if(address <= 0x041f'ffff) return rdp.write<Size>(address, data);
|
||||
if(address <= 0x042f'ffff) return rdp.io.write<Size>(address, data);
|
||||
if(address <= 0x043f'ffff) return mi.write<Size>(address, data);
|
||||
if(address <= 0x044f'ffff) return vi.write<Size>(address, data);
|
||||
if(address <= 0x045f'ffff) return ai.write<Size>(address, data);
|
||||
if(address <= 0x046f'ffff) return pi.write<Size>(address, data);
|
||||
if(address <= 0x047f'ffff) return ri.write<Size>(address, data);
|
||||
if(address <= 0x048f'ffff) return si.write<Size>(address, data);
|
||||
if(address <= 0x03ff'ffff) return rdram.write<Size>(address, data, cycles);
|
||||
if(address <= 0x0407'ffff) return rsp.write<Size>(address, data, cycles);
|
||||
if(address <= 0x040f'ffff) return rsp.status.write<Size>(address, data, cycles);
|
||||
if(address <= 0x041f'ffff) return rdp.write<Size>(address, data, cycles);
|
||||
if(address <= 0x042f'ffff) return rdp.io.write<Size>(address, data, cycles);
|
||||
if(address <= 0x043f'ffff) return mi.write<Size>(address, data, cycles);
|
||||
if(address <= 0x044f'ffff) return vi.write<Size>(address, data, cycles);
|
||||
if(address <= 0x045f'ffff) return ai.write<Size>(address, data, cycles);
|
||||
if(address <= 0x046f'ffff) return pi.write<Size>(address, data, cycles);
|
||||
if(address <= 0x047f'ffff) return ri.write<Size>(address, data, cycles);
|
||||
if(address <= 0x048f'ffff) return si.write<Size>(address, data, cycles);
|
||||
if(address <= 0x04ff'ffff) return;
|
||||
if(address <= 0x1fbf'ffff) return pi.write<Size>(address, data);
|
||||
if(address <= 0x1fcf'ffff) return pif.write<Size>(address, data);
|
||||
if(address <= 0x7fff'ffff) return pi.write<Size>(address, data);
|
||||
if(address <= 0x1fbf'ffff) return pi.write<Size>(address, data, cycles);
|
||||
if(address <= 0x1fcf'ffff) return si.write<Size>(address, data, cycles);
|
||||
if(address <= 0x7fff'ffff) return pi.write<Size>(address, data, cycles);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
template<typename T>
|
||||
struct IO {
|
||||
struct RCP { //A device which is part of RCP
|
||||
const u32 DefaultReadCycles = 20;
|
||||
const u32 DefaultWriteCycles = 0; //not implemented until we implement the CPU write queue
|
||||
|
||||
template<u32 Size>
|
||||
auto read(u32 address) -> u64 {
|
||||
auto read(u32 address, u32& cycles) -> u64 {
|
||||
cycles = DefaultReadCycles;
|
||||
if constexpr(Size == Byte) {
|
||||
auto data = ((T*)this)->readWord(address);
|
||||
auto data = ((T*)this)->readWord(address, cycles);
|
||||
switch(address & 3) {
|
||||
case 0: return data >> 24;
|
||||
case 1: return data >> 16;
|
||||
|
@ -12,43 +16,92 @@ struct IO {
|
|||
}
|
||||
}
|
||||
if constexpr(Size == Half) {
|
||||
auto data = ((T*)this)->readWord(address);
|
||||
auto data = ((T*)this)->readWord(address, cycles);
|
||||
switch(address & 2) {
|
||||
case 0: return data >> 16;
|
||||
case 2: return data >> 0;
|
||||
}
|
||||
}
|
||||
if constexpr(Size == Word) {
|
||||
return ((T*)this)->readWord(address);
|
||||
return ((T*)this)->readWord(address, cycles);
|
||||
}
|
||||
if constexpr(Size == Dual) {
|
||||
u64 data = ((T*)this)->readWord(address);
|
||||
return data << 32 | ((T*)this)->readWord(address + 4);
|
||||
u64 data = ((T*)this)->readWord(address, cycles);
|
||||
return data << 32 | ((T*)this)->readWord(address + 4, cycles);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto write(u32 address, u64 data) -> void {
|
||||
auto write(u32 address, u64 data, u32& cycles) -> void {
|
||||
cycles = DefaultWriteCycles;
|
||||
if constexpr(Size == Byte) {
|
||||
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);
|
||||
case 0: return ((T*)this)->writeWord(address, data << 24, cycles);
|
||||
case 1: return ((T*)this)->writeWord(address, data << 16, cycles);
|
||||
case 2: return ((T*)this)->writeWord(address, data << 8, cycles);
|
||||
case 3: return ((T*)this)->writeWord(address, data << 0, cycles);
|
||||
}
|
||||
}
|
||||
if constexpr(Size == Half) {
|
||||
switch(address & 2) {
|
||||
case 0: return ((T*)this)->writeWord(address, data << 16);
|
||||
case 2: return ((T*)this)->writeWord(address, data << 0);
|
||||
case 0: return ((T*)this)->writeWord(address, data << 16, cycles);
|
||||
case 2: return ((T*)this)->writeWord(address, data << 0, cycles);
|
||||
}
|
||||
}
|
||||
if constexpr(Size == Word) {
|
||||
((T*)this)->writeWord(address, data);
|
||||
((T*)this)->writeWord(address, data, cycles);
|
||||
}
|
||||
if constexpr(Size == Dual) {
|
||||
((T*)this)->writeWord(address, data >> 32);
|
||||
((T*)this)->writeWord(address, data >> 32, cycles);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct PI { //A device which is reachable only behind PI
|
||||
template<u32 Size>
|
||||
auto read(u32 address) -> u64 {
|
||||
static_assert(Size == Half || Size == Word); //PI bus will do 32-bit (CPU) or 16-bit (DMA) only
|
||||
if constexpr(Size == Half) {
|
||||
return ((T*)this)->readHalf(address);
|
||||
}
|
||||
if constexpr(Size == Word) {
|
||||
return ((T*)this)->readWord(address);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto write(u32 address, u64 data) -> void {
|
||||
static_assert(Size == Half || Size == Word); //PI bus will do 32-bit (CPU) or 16-bit (DMA) only
|
||||
if constexpr(Size == Half) {
|
||||
return ((T*)this)->writeHalf(address, data);
|
||||
}
|
||||
if constexpr(Size == Word) {
|
||||
return ((T*)this)->writeWord(address, data);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SI { //A device which is reachable only behind SI
|
||||
template<u32 Size>
|
||||
auto read(u32 address) -> u64 {
|
||||
static_assert(Size == Word); //SI bus will do 32-bit (CPU/DMA)
|
||||
if constexpr(Size == Word) {
|
||||
return ((T*)this)->readWord(address);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto write(u32 address, u64 data) -> void {
|
||||
static_assert(Size == Word); //PI bus will do 32-bit (CPU/DMA)
|
||||
if constexpr(Size == Word) {
|
||||
return ((T*)this)->writeWord(address, data);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,8 +31,8 @@ namespace Memory {
|
|||
|
||||
struct Bus {
|
||||
//bus.hpp
|
||||
template<u32 Size> auto read(u32 address) -> u64;
|
||||
template<u32 Size> auto write(u32 address, u64 data) -> void;
|
||||
template<u32 Size> auto read(u32 address, u32& cycles) -> u64;
|
||||
template<u32 Size> auto write(u32 address, u64 data, u32& cycles) -> void;
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto MI::readWord(u32 address) -> u32 {
|
||||
auto MI::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -42,7 +42,7 @@ auto MI::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto MI::writeWord(u32 address, u32 data_) -> void {
|
||||
auto MI::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//MIPS Interface
|
||||
|
||||
struct MI : Memory::IO<MI> {
|
||||
struct MI : Memory::RCP<MI> {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
|
@ -27,8 +27,8 @@ struct MI : Memory::IO<MI> {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -7,21 +7,18 @@
|
|||
#include <ares/ares.hpp>
|
||||
#include <nall/float-env.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/queue.hpp>
|
||||
#include <nall/recompiler/generic/generic.hpp>
|
||||
#include <component/processor/sm5k/sm5k.hpp>
|
||||
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
#include <nmmintrin.h>
|
||||
using v128 = __m128i;
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
#elif defined(ARCHITECTURE_ARM64) && !defined(COMPILER_MICROSOFT)
|
||||
#include <sse2neon.h>
|
||||
using v128 = __m128i;
|
||||
#endif
|
||||
|
||||
#if defined(VULKAN)
|
||||
#include <n64/vulkan/vulkan.hpp>
|
||||
#endif
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
auto enumerate() -> vector<string>;
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
|
@ -57,6 +54,8 @@ namespace ares::Nintendo64 {
|
|||
PI_BUS_Write,
|
||||
SI_DMA_Read,
|
||||
SI_DMA_Write,
|
||||
SI_BUS_Write,
|
||||
RTC_Tick,
|
||||
DD_Clock_Tick,
|
||||
DD_MECHA_Response,
|
||||
DD_BM_Request,
|
||||
|
@ -65,10 +64,16 @@ namespace ares::Nintendo64 {
|
|||
};
|
||||
extern Queue queue;
|
||||
|
||||
struct BCD {
|
||||
static auto encode(u8 value) -> u8 { return value / 10 << 4 | value % 10; }
|
||||
static auto decode(u8 value) -> u8 { return (value >> 4) * 10 + (value & 15); }
|
||||
};
|
||||
|
||||
#include <n64/accuracy.hpp>
|
||||
#include <n64/memory/memory.hpp>
|
||||
#include <n64/system/system.hpp>
|
||||
#include <n64/cartridge/cartridge.hpp>
|
||||
#include <n64/cic/cic.hpp>
|
||||
#include <n64/controller/controller.hpp>
|
||||
#include <n64/dd/dd.hpp>
|
||||
#include <n64/mi/mi.hpp>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
inline auto PI::readWord(u32 address) -> u32 {
|
||||
inline auto PI::readWord(u32 address, u32& cycles) -> u32 {
|
||||
if(address <= 0x046f'ffff) return ioRead(address);
|
||||
|
||||
if (unlikely(io.ioBusy)) {
|
||||
writeForceFinish(); //technically, we should wait until Queue::PI_BUS_Write
|
||||
cycles += writeForceFinish();
|
||||
return io.busLatch;
|
||||
}
|
||||
cycles += 250;
|
||||
return busRead<Word>(address);
|
||||
}
|
||||
|
||||
|
@ -50,7 +51,7 @@ inline auto PI::busRead(u32 address) -> u32 {
|
|||
return unmapped; //accesses here actually lock out the RCP
|
||||
}
|
||||
|
||||
inline auto PI::writeWord(u32 address, u32 data) -> void {
|
||||
inline auto PI::writeWord(u32 address, u32 data, u32& cycles) -> void {
|
||||
if(address <= 0x046f'ffff) return ioWrite(address, data);
|
||||
|
||||
if(io.ioBusy) return;
|
||||
|
@ -106,7 +107,7 @@ inline auto PI::writeFinished() -> void {
|
|||
io.ioBusy = 0;
|
||||
}
|
||||
|
||||
inline auto PI::writeForceFinish() -> void {
|
||||
inline auto PI::writeForceFinish() -> u32 {
|
||||
io.ioBusy = 0;
|
||||
queue.remove(Queue::PI_BUS_Write);
|
||||
return queue.remove(Queue::PI_BUS_Write);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Peripheral Interface
|
||||
|
||||
struct PI : Memory::IO<PI> {
|
||||
struct PI : Memory::RCP<PI> {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
|
@ -28,10 +28,10 @@ struct PI : Memory::IO<PI> {
|
|||
auto ioWrite(u32 address, u32 data) -> void;
|
||||
|
||||
//bus.hpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
auto writeFinished() -> void;
|
||||
auto writeForceFinish() -> void;
|
||||
auto writeForceFinish() -> u32;
|
||||
template <u32 Size>
|
||||
auto busRead(u32 address) -> u32;
|
||||
template <u32 Size>
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
|
||||
auto PIF::addressCRC(u16 address) const -> n5 {
|
||||
n5 crc = 0;
|
||||
for(u32 i : range(16)) {
|
||||
n5 xor_ = crc & 0x10 ? 0x15 : 0x00;
|
||||
crc <<= 1;
|
||||
if(address & 0x8000) crc |= 1;
|
||||
address <<= 1;
|
||||
crc ^= xor_;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
auto PIF::dataCRC(array_view<u8> data) const -> n8 {
|
||||
n8 crc = 0;
|
||||
for(u32 i : range(33)) {
|
||||
for(u32 j : reverse(range(8))) {
|
||||
n8 xor_ = crc & 0x80 ? 0x85 : 0x00;
|
||||
crc <<= 1;
|
||||
if(i < 32) {
|
||||
if(data[i] & 1 << j) crc |= 1;
|
||||
}
|
||||
crc ^= xor_;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
auto PIF::descramble(n4 *buf, int size) -> void {
|
||||
for(int i=size-1; i>0; i--) buf[i] -= buf[i-1] + 1;
|
||||
}
|
||||
|
||||
auto PIF::step(u32 clocks) -> void {
|
||||
Thread::clock += clocks;
|
||||
if(intram.bootTimeout > 0) intram.bootTimeout -= clocks;
|
||||
}
|
||||
|
||||
auto PIF::ramReadCommand() -> u8 {
|
||||
return ram.read<Byte>(0x3f);
|
||||
}
|
||||
|
||||
auto PIF::ramWriteCommand(u8 val) -> void {
|
||||
return ram.write<Byte>(0x3f, val);
|
||||
}
|
||||
|
||||
auto PIF::memSwap(u32 address, n8 &val) -> void {
|
||||
n8 data = ram.read<Byte>(address);
|
||||
ram.write<Byte>(address, (u8)val);
|
||||
val = data;
|
||||
}
|
||||
|
||||
auto PIF::memSwapSecrets() -> void {
|
||||
for (auto i: range(3)) memSwap(0x25+i, intram.osInfo[i]);
|
||||
for (auto i: range(6)) memSwap(0x32+i, intram.cpuChecksum[i]);
|
||||
}
|
||||
|
||||
auto PIF::intA(bool dir, bool size) -> void {
|
||||
if(dir == Read) {
|
||||
if(size == Size64) {
|
||||
if(ramReadCommand() & 0x02) {
|
||||
challenge();
|
||||
return;
|
||||
}
|
||||
joyRun();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(dir == Write) {
|
||||
if(ramReadCommand() & 0x01) {
|
||||
ramWriteCommand(ramReadCommand() & ~0x01);
|
||||
joyInit();
|
||||
joyParse();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto PIF::joyInit() -> void {
|
||||
for(auto i : range(5)) {
|
||||
intram.joyStatus[i].skip = 1;
|
||||
intram.joyStatus[i].reset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto PIF::joyParse() -> void {
|
||||
static constexpr bool Debug = 0;
|
||||
|
||||
if constexpr(Debug) {
|
||||
print("joyParse:\n{\n");
|
||||
for(u32 y : range(8)) {
|
||||
print(" ");
|
||||
for(u32 x : range(8)) {
|
||||
print(hex(ram.read<Byte>(y * 8 + x), 2L), " ");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
print("}\n");
|
||||
}
|
||||
|
||||
u32 offset = 0;
|
||||
n3 channel = 0; //0-5
|
||||
while(channel < 5 && offset < 64) {
|
||||
n8 send = ram.read<Byte>(offset++);
|
||||
if(send == 0xfe) break; //end of packets
|
||||
if(send == 0xff) continue; //alignment padding
|
||||
if(send == 0x00) { channel++; continue; } // channel skip
|
||||
if(send == 0xfd) { // channel reset
|
||||
intram.joyStatus[channel++].reset = 1;
|
||||
continue;
|
||||
}
|
||||
u32 sendOffset = offset-1;
|
||||
n8 recv = ram.read<Byte>(offset++);
|
||||
send &= 0x3f;
|
||||
recv &= 0x3f;
|
||||
offset += send+recv;
|
||||
if(offset < 64) {
|
||||
intram.joyAddress[channel] = sendOffset;
|
||||
intram.joyStatus[channel].skip = 0;
|
||||
channel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto PIF::joyRun() -> void {
|
||||
static constexpr bool Debug = 0;
|
||||
|
||||
ControllerPort* controllers[4] = {
|
||||
&controllerPort1,
|
||||
&controllerPort2,
|
||||
&controllerPort3,
|
||||
&controllerPort4,
|
||||
};
|
||||
|
||||
for (i32 channel=4; channel>=0; channel--) {
|
||||
if (intram.joyStatus[channel].reset) {
|
||||
if (channel < 4 && controllers[channel]->device)
|
||||
controllers[channel]->device->reset();
|
||||
continue;
|
||||
}
|
||||
if (intram.joyStatus[channel].skip) continue;
|
||||
|
||||
u32 offset = intram.joyAddress[channel];
|
||||
n8 send = ram.read<Byte>(offset++);
|
||||
if(send & 0x80) continue; // skip (another way to do it)
|
||||
if(send & 0x40) { //reset (another way to do it)
|
||||
if (channel < 4 && controllers[channel]->device)
|
||||
controllers[channel]->device->reset();
|
||||
continue;
|
||||
}
|
||||
u32 recvOffset = offset;
|
||||
n8 recv = ram.read<Byte>(offset++);
|
||||
send &= 0x3f;
|
||||
recv &= 0x3f;
|
||||
|
||||
n8 input[64];
|
||||
for(u32 index : range(send)) {
|
||||
input[index] = ram.read<Byte>(offset++);
|
||||
}
|
||||
n8 output[64];
|
||||
b1 valid = 0;
|
||||
b1 over = 0;
|
||||
|
||||
//controller port communication
|
||||
if (channel < 4 && controllers[channel]->device) {
|
||||
n2 status = controllers[channel]->device->comm(send, recv, input, output);
|
||||
valid = status.bit(0);
|
||||
over = status.bit(1);
|
||||
}
|
||||
//cartrige joybus communication
|
||||
if (channel == 4) {
|
||||
n2 status = cartridge.joybusComm(send, recv, input, output);
|
||||
valid = status.bit(0);
|
||||
over = status.bit(1);
|
||||
}
|
||||
|
||||
if(!valid) ram.write<Byte>(recvOffset, 0x80 | recv);
|
||||
if(over) ram.write<Byte>(recvOffset, 0x40 | recv);
|
||||
if (valid) {
|
||||
for(u32 index : range(recv)) {
|
||||
ram.write<Byte>(offset++, output[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(Debug) {
|
||||
print("joyRun:\n[\n");
|
||||
for(u32 y : range(8)) {
|
||||
print(" ");
|
||||
for(u32 x : range(8)) {
|
||||
print(hex(ram.read<Byte>(y * 8 + x), 2L), " ");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
print("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
auto PIF::estimateTiming() -> u32 {
|
||||
ControllerPort* controllers[4] = {
|
||||
&controllerPort1,
|
||||
&controllerPort2,
|
||||
&controllerPort3,
|
||||
&controllerPort4,
|
||||
};
|
||||
|
||||
u32 cycles = 13600;
|
||||
u32 short_cmds = 0;
|
||||
|
||||
u32 offset = 0;
|
||||
u32 channel = 0;
|
||||
while(offset < 64 && channel < 5) {
|
||||
n8 send = ram.read<Byte>(offset++);
|
||||
if(send == 0xfe) { short_cmds++; break; } //end of packets
|
||||
if(send == 0x00) { short_cmds++; channel++; continue; }
|
||||
if(send == 0xfd) { short_cmds++; channel++; continue; } //channel reset
|
||||
if(send == 0xff) { short_cmds++; continue; } //alignment padding
|
||||
|
||||
n8 recv = ram.read<Byte>(offset++);
|
||||
|
||||
//clear flags from lengths
|
||||
send &= 0x3f;
|
||||
recv &= 0x3f;
|
||||
n8 input[64];
|
||||
for(u32 index : range(send)) {
|
||||
input[index] = ram.read<Byte>(offset++);
|
||||
}
|
||||
offset += recv;
|
||||
|
||||
if (channel < 4) {
|
||||
if (controllers[channel]->device) {
|
||||
cycles += 22000;
|
||||
} else {
|
||||
cycles += 18000;
|
||||
}
|
||||
} else {
|
||||
//accessories(TBD)
|
||||
cycles += 20000;
|
||||
}
|
||||
|
||||
channel++;
|
||||
}
|
||||
|
||||
cycles += 1420 * short_cmds;
|
||||
return cycles;
|
||||
}
|
||||
|
||||
auto PIF::challenge() -> void {
|
||||
cic.writeBit(1); cic.writeBit(0); //challenge command
|
||||
cic.readNibble(); //ignore timeout value returned by CIC (we simulate instant response)
|
||||
cic.readNibble(); //timeout high nibble
|
||||
for(u32 address : range(15)) {
|
||||
auto data = ram.read<Byte>(0x30 + address);
|
||||
cic.writeNibble(data >> 4 & 0xf);
|
||||
cic.writeNibble(data >> 0 & 0xf);
|
||||
}
|
||||
cic.readBit(); //ignore start bit
|
||||
for(u32 address : range(15)) {
|
||||
u8 data = 0;
|
||||
data |= cic.readNibble() << 4;
|
||||
data |= cic.readNibble() << 0;
|
||||
ram.write<Byte>(0x30 + address, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto PIF::mainHLE() -> void {
|
||||
step(10240*8);
|
||||
|
||||
if(likely(state == Run)) {
|
||||
//cicCompare()
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == Init) {
|
||||
n4 hello = cic.readNibble();
|
||||
if (hello.bit(0,1) != 1) {
|
||||
debug(unusual, "[PIF::main] invalid CIC hello message ", hex(hello, 4L));
|
||||
state = Error;
|
||||
return;
|
||||
}
|
||||
if constexpr(Accuracy::PIF::RegionLock) {
|
||||
if(hello.bit(2) != (u32)system.region()) {
|
||||
const char *region[2] = { "NTSC", "PAL" };
|
||||
debug(unusual, "[PIF::main] CIC region mismatch: console is ", region[(u32)system.region()], " but cartridge is ", region[(int)hello.bit(4)]);
|
||||
state = Error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
n4 osinfo = 0;
|
||||
osinfo.bit(2) = 1; //"version" bit (unknown, always set)
|
||||
osinfo.bit(3) = hello.bit(3); //64dd
|
||||
|
||||
n4 buf[6];
|
||||
for (auto i: range(6)) buf[i] = cic.readNibble();
|
||||
for (auto i: range(2)) descramble(buf, 6);
|
||||
|
||||
intram.osInfo[0].bit(4,7) = buf[0];
|
||||
intram.osInfo[0].bit(0,3) = buf[1];
|
||||
intram.osInfo[1].bit(4,7) = buf[2];
|
||||
intram.osInfo[1].bit(0,3) = buf[3];
|
||||
intram.osInfo[2].bit(4,7) = buf[4];
|
||||
intram.osInfo[2].bit(0,3) = buf[5];
|
||||
|
||||
intram.osInfo[0].bit(0,3) = osinfo;
|
||||
ramWriteCommand(0x00);
|
||||
memSwapSecrets(); //show osinfo+seeds in external memory
|
||||
state = WaitLockout;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == WaitLockout && (ramReadCommand() & 0x10)) {
|
||||
io.romLockout = 1;
|
||||
joyInit();
|
||||
state = WaitGetChecksum;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == WaitGetChecksum && (ramReadCommand() & 0x20)) {
|
||||
memSwapSecrets(); //hide osinfo+seeds, copy+hide checksum to internal memory
|
||||
ramWriteCommand(ramReadCommand() | 0x80);
|
||||
state = WaitCheckChecksum;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == WaitCheckChecksum && (ramReadCommand() & 0x40)) {
|
||||
if (true) { // only on cold boot
|
||||
n4 buf[16];
|
||||
for (auto i: range(16)) buf[i] = cic.readNibble();
|
||||
for (auto i: range(4)) descramble(buf, 16);
|
||||
for (auto i: range(6)) {
|
||||
intram.cicChecksum[i].bit(4,7) = buf[i*2+4];
|
||||
intram.cicChecksum[i].bit(0,3) = buf[i*2+5];
|
||||
}
|
||||
intram.osInfo[0].bit(1) = 1; //warm boot (NMI) flag (ready in case a reset is made in the future)
|
||||
}
|
||||
|
||||
for (auto i: range(6)) {
|
||||
u8 data = intram.cpuChecksum[i];
|
||||
if (intram.cicChecksum[i] != data) {
|
||||
debug(unusual, "[PIF::main] invalid IPL2 checksum: ", cic.model, ":",
|
||||
hex(intram.cicChecksum[0], 2L), hex(intram.cicChecksum[1], 2L), hex(intram.cicChecksum[2], 2L),
|
||||
hex(intram.cicChecksum[3], 2L), hex(intram.cicChecksum[4], 2L), hex(intram.cicChecksum[5], 2L),
|
||||
" != cpu:",
|
||||
hex(intram.cpuChecksum[0], 2L), hex(intram.cpuChecksum[1], 2L), hex(intram.cpuChecksum[2], 2L),
|
||||
hex(intram.cpuChecksum[3], 2L), hex(intram.cpuChecksum[4], 2L), hex(intram.cpuChecksum[5], 2L));
|
||||
state = Error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto i: range(6)) intram.cpuChecksum[i] = 0;
|
||||
state = WaitTerminateBoot;
|
||||
intram.bootTimeout = 6 * 187500000; //6 seconds
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == WaitTerminateBoot && (ramReadCommand() & 0x08)) {
|
||||
ramWriteCommand(0x00);
|
||||
io.resetEnabled = 1;
|
||||
state = Run;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == WaitTerminateBoot && intram.bootTimeout <= 0) {
|
||||
debug(unusual, "[PIF::main] boot timeout: CPU has not sent the boot termination command within 5 seconds. Halting the CPU");
|
||||
state = Error;
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == Error) {
|
||||
cpu.scc.nmiPending = 1;
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
auto PIF::readWord(u32 address) -> u32 {
|
||||
auto PIF::readInt(u32 address) -> u32 {
|
||||
address &= 0x7ff;
|
||||
if(address <= 0x7bf) {
|
||||
if(io.romLockout) return 0;
|
||||
|
@ -7,7 +7,7 @@ auto PIF::readWord(u32 address) -> u32 {
|
|||
return ram.read<Word>(address);
|
||||
}
|
||||
|
||||
auto PIF::writeWord(u32 address, u32 data) -> void {
|
||||
auto PIF::writeInt(u32 address, u32 data) -> void {
|
||||
address &= 0x7ff;
|
||||
if(address <= 0x7bf) {
|
||||
if(io.romLockout) return;
|
||||
|
@ -15,3 +15,29 @@ auto PIF::writeWord(u32 address, u32 data) -> void {
|
|||
}
|
||||
return ram.write<Word>(address, data);
|
||||
}
|
||||
|
||||
auto PIF::readWord(u32 address) -> u32 {
|
||||
intA(Read, Size4);
|
||||
return readInt(address);
|
||||
}
|
||||
|
||||
auto PIF::writeWord(u32 address, u32 data) -> void {
|
||||
writeInt(address, data);
|
||||
return intA(Write, Size4);
|
||||
}
|
||||
|
||||
auto PIF::dmaRead(u32 address, u32 ramAddress) -> void {
|
||||
intA(Read, Size64);
|
||||
for(u32 offset = 0; offset < 64; offset += 4) {
|
||||
u32 data = readInt(address + offset);
|
||||
rdram.ram.write<Word>(ramAddress + offset, data);
|
||||
}
|
||||
}
|
||||
|
||||
auto PIF::dmaWrite(u32 address, u32 ramAddress) -> void {
|
||||
for(u32 offset = 0; offset < 64; offset += 4) {
|
||||
u32 data = rdram.ram.read<Word>(ramAddress + offset);
|
||||
writeInt(address + offset, data);
|
||||
}
|
||||
intA(Write, Size64);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace ares::Nintendo64 {
|
||||
|
||||
PIF pif;
|
||||
#include "hle.cpp"
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
@ -22,262 +23,13 @@ auto PIF::unload() -> void {
|
|||
node.reset();
|
||||
}
|
||||
|
||||
auto PIF::addressCRC(u16 address) const -> n5 {
|
||||
n5 crc = 0;
|
||||
for(u32 i : range(16)) {
|
||||
n5 xor = crc & 0x10 ? 0x15 : 0x00;
|
||||
crc <<= 1;
|
||||
if(address & 0x8000) crc |= 1;
|
||||
address <<= 1;
|
||||
crc ^= xor;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
auto PIF::dataCRC(array_view<u8> data) const -> n8 {
|
||||
n8 crc = 0;
|
||||
for(u32 i : range(33)) {
|
||||
for(u32 j : reverse(range(8))) {
|
||||
n8 xor = crc & 0x80 ? 0x85 : 0x00;
|
||||
crc <<= 1;
|
||||
if(i < 32) {
|
||||
if(data[i] & 1 << j) crc |= 1;
|
||||
}
|
||||
crc ^= xor;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
auto PIF::run() -> void {
|
||||
auto flags = ram.read<Byte>(0x3f);
|
||||
|
||||
//controller polling
|
||||
if(flags & 0x01) {
|
||||
//todo: this flag is supposed to be cleared, but doing so breaks inputs
|
||||
//flags &= ~0x01;
|
||||
scan();
|
||||
}
|
||||
|
||||
//CIC-NUS-6105 challenge/response
|
||||
if(flags & 0x02) {
|
||||
flags &= ~0x02;
|
||||
challenge();
|
||||
}
|
||||
|
||||
//unknown purpose
|
||||
if(flags & 0x04) {
|
||||
flags &= ~0x04;
|
||||
debug(unimplemented, "[SI::main] flags & 0x04");
|
||||
}
|
||||
|
||||
//must be sent within 5s of the console booting, or SM5 will lock the N64
|
||||
if(flags & 0x08) {
|
||||
flags &= ~0x08;
|
||||
}
|
||||
|
||||
//PIF ROM lockout
|
||||
if(flags & 0x10) {
|
||||
flags &= ~0x10;
|
||||
io.romLockout = 1;
|
||||
}
|
||||
|
||||
//initialization
|
||||
if(flags & 0x20) {
|
||||
flags &= ~0x20;
|
||||
flags |= 0x80; //set completion flag
|
||||
}
|
||||
|
||||
//clear PIF RAM
|
||||
if(flags & 0x40) {
|
||||
flags &= ~0x40;
|
||||
ram.fill();
|
||||
}
|
||||
|
||||
ram.write<Byte>(0x3f, flags);
|
||||
}
|
||||
|
||||
auto PIF::scan() -> void {
|
||||
ControllerPort* controllers[4] = {
|
||||
&controllerPort1,
|
||||
&controllerPort2,
|
||||
&controllerPort3,
|
||||
&controllerPort4,
|
||||
};
|
||||
|
||||
static constexpr bool Debug = 0;
|
||||
|
||||
if constexpr(Debug) {
|
||||
print("{\n");
|
||||
for(u32 y : range(8)) {
|
||||
print(" ");
|
||||
for(u32 x : range(8)) {
|
||||
print(hex(ram.read<Byte>(y * 8 + x), 2L), " ");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
print("}\n");
|
||||
}
|
||||
|
||||
n3 channel = 0; //0-5
|
||||
for(u32 offset = 0; offset < 64;) {
|
||||
n8 send = ram.read<Byte>(offset++);
|
||||
if(send == 0x00) { channel++; continue; }
|
||||
if(send == 0xfd) continue; //channel reset
|
||||
if(send == 0xfe) break; //end of packets
|
||||
if(send == 0xff) continue; //alignment padding
|
||||
n8 recvOffset = offset;
|
||||
n8 recv = ram.read<Byte>(offset++);
|
||||
if(recv == 0xfe) break; //end of packets
|
||||
|
||||
//clear flags from lengths
|
||||
send &= 0x3f;
|
||||
recv &= 0x3f;
|
||||
|
||||
n8 input[64];
|
||||
for(u32 index : range(send)) {
|
||||
input[index] = ram.read<Byte>(offset++);
|
||||
}
|
||||
n8 output[64];
|
||||
b1 valid = 0;
|
||||
b1 over = 0;
|
||||
|
||||
//controller port communication
|
||||
if (channel < 4 && controllers[channel]->device) {
|
||||
n2 status = controllers[channel]->device->comm(send, recv, input, output);
|
||||
valid = status.bit(0);
|
||||
over = status.bit(1);
|
||||
}
|
||||
|
||||
if (channel >= 4) {
|
||||
//status
|
||||
if(input[0] == 0x00 || input[0] == 0xff) {
|
||||
//cartridge EEPROM (4kbit)
|
||||
if(cartridge.eeprom.size == 512) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0x80;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//cartridge EEPROM (16kbit)
|
||||
if(cartridge.eeprom.size == 2048) {
|
||||
output[0] = 0x00;
|
||||
output[1] = 0xc0;
|
||||
output[2] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//read EEPROM
|
||||
if(input[0] == 0x04 && send >= 2) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(recv)) {
|
||||
output[index] = cartridge.eeprom.read<Byte>(address++);
|
||||
}
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//write EEPROM
|
||||
if(input[0] == 0x05 && send >= 2 && recv >= 1) {
|
||||
u32 address = input[1] * 8;
|
||||
for(u32 index : range(send - 2)) {
|
||||
cartridge.eeprom.write<Byte>(address++, input[2 + index]);
|
||||
}
|
||||
output[0] = 0x00;
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
//RTC status
|
||||
if(input[0] == 0x06) {
|
||||
debug(unimplemented, "[SI::main] RTC status");
|
||||
}
|
||||
|
||||
//RTC read
|
||||
if(input[0] == 0x07) {
|
||||
debug(unimplemented, "[SI::main] RTC read");
|
||||
}
|
||||
|
||||
//RTC write
|
||||
if(input[0] == 0x08) {
|
||||
debug(unimplemented, "[SI::main] RTC write");
|
||||
}
|
||||
}
|
||||
|
||||
if(!valid) {
|
||||
ram.write<Byte>(recvOffset, 0x80 | recv & 0x3f);
|
||||
}
|
||||
if(over) {
|
||||
ram.write<Byte>(recvOffset, 0x40 | recv & 0x3f);
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
for(u32 index : range(recv)) {
|
||||
ram.write<Byte>(offset++, output[index]);
|
||||
}
|
||||
}
|
||||
channel++;
|
||||
}
|
||||
|
||||
if constexpr(Debug) {
|
||||
print("[\n");
|
||||
for(u32 y : range(8)) {
|
||||
print(" ");
|
||||
for(u32 x : range(8)) {
|
||||
print(hex(ram.read<Byte>(y * 8 + x), 2L), " ");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
print("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
//CIC-NUS-6105 anti-piracy challenge/response
|
||||
auto PIF::challenge() -> void {
|
||||
static n4 lut[32] = {
|
||||
0x4, 0x7, 0xa, 0x7, 0xe, 0x5, 0xe, 0x1,
|
||||
0xc, 0xf, 0x8, 0xf, 0x6, 0x3, 0x6, 0x9,
|
||||
0x4, 0x1, 0xa, 0x7, 0xe, 0x5, 0xe, 0x1,
|
||||
0xc, 0x9, 0x8, 0x5, 0x6, 0x3, 0xc, 0x9,
|
||||
};
|
||||
|
||||
n4 challenge[30];
|
||||
n4 response[30];
|
||||
|
||||
//15 bytes -> 30 nibbles
|
||||
for(u32 address : range(15)) {
|
||||
auto data = ram.read<Byte>(0x30 + address);
|
||||
challenge[address << 1 | 0] = data >> 4;
|
||||
challenge[address << 1 | 1] = data >> 0;
|
||||
}
|
||||
|
||||
n4 key = 0xb;
|
||||
n1 sel = 0;
|
||||
for(u32 address : range(30)) {
|
||||
n4 data = key + 5 * challenge[address];
|
||||
response[address] = data;
|
||||
key = lut[sel << 4 | data];
|
||||
n1 mod = data >> 3;
|
||||
n3 mag = data >> 0;
|
||||
if(mod) mag = ~mag;
|
||||
if(mag % 3 != 1) mod = !mod;
|
||||
if(sel) {
|
||||
if(data == 0x1 || data == 0x9) mod = 1;
|
||||
if(data == 0xb || data == 0xe) mod = 0;
|
||||
}
|
||||
sel = mod;
|
||||
}
|
||||
|
||||
//30 nibbles -> 15 bytes
|
||||
for(u32 address : range(15)) {
|
||||
n8 data = 0;
|
||||
data |= response[address << 1 | 0] << 4;
|
||||
data |= response[address << 1 | 1] << 0;
|
||||
ram.write<Byte>(0x30 + address, data);
|
||||
}
|
||||
auto PIF::main() -> void {
|
||||
mainHLE();
|
||||
}
|
||||
|
||||
auto PIF::power(bool reset) -> void {
|
||||
Thread::reset();
|
||||
|
||||
string pifrom = Region::PAL() ? "pif.pal.rom" : "pif.ntsc.rom";
|
||||
if(auto fp = system.pak->read(pifrom)) {
|
||||
rom.load(fp);
|
||||
|
@ -285,27 +37,8 @@ auto PIF::power(bool reset) -> void {
|
|||
|
||||
ram.fill();
|
||||
io = {};
|
||||
|
||||
//write CIC seeds into PIF RAM so that cartridge checksum function passes
|
||||
string cic = cartridge.node ? cartridge.cic() : dd.cic();
|
||||
n8 seed = 0x3f;
|
||||
n1 version = 0;
|
||||
n1 type = 0;
|
||||
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") seed = 0x3f, version = 1;
|
||||
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") seed = 0x3f;
|
||||
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") seed = 0x78;
|
||||
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") seed = 0x91;
|
||||
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") seed = 0x85;
|
||||
if(cic == "CIC-NUS-8303" || cic == "CIC-NUS-8401") seed = 0xdd, type = 1;
|
||||
if(cic == "CIC-NUS-DDUS") seed = 0xde, type = 1;
|
||||
|
||||
n32 data;
|
||||
data.bit(0, 7) = 0x3f; //CIC IPL2 seed
|
||||
data.bit(8,15) = seed; //CIC IPL3 seed
|
||||
data.bit(17) = reset; //osResetType (0 = power; 1 = reset (NMI))
|
||||
data.bit(18) = version; //osVersion
|
||||
data.bit(19) = type; //osRomType (0 = Gamepak; 1 = 64DD)
|
||||
ram.write<Word>(0x24, data);
|
||||
intram = {};
|
||||
state = Init;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
//PIF-NUS
|
||||
|
||||
struct PIF : Memory::IO<PIF> {
|
||||
struct PIF : Thread, Memory::SI<PIF> {
|
||||
enum State : u32 { Init, WaitLockout, WaitGetChecksum, WaitCheckChecksum, WaitTerminateBoot, Run, Error };
|
||||
enum IntADir : bool { Read, Write };
|
||||
enum IntASize : bool { Size4, Size64 };
|
||||
|
||||
Node::Object node;
|
||||
Memory::Readable rom;
|
||||
Memory::Writable ram;
|
||||
u32 state;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
|
@ -13,28 +18,59 @@ struct PIF : Memory::IO<PIF> {
|
|||
struct Memory {
|
||||
Node::Debugger::Memory ram;
|
||||
} memory;
|
||||
|
||||
} debugger;
|
||||
|
||||
struct Intram {
|
||||
n8 osInfo[3];
|
||||
n8 cpuChecksum[6];
|
||||
n8 cicChecksum[6];
|
||||
s32 bootTimeout;
|
||||
n8 joyAddress[5];
|
||||
struct {
|
||||
n1 skip;
|
||||
n1 reset;
|
||||
} joyStatus[5];
|
||||
|
||||
auto serialize(serializer& s) -> void;
|
||||
} intram;
|
||||
|
||||
//pif.cpp
|
||||
auto step(u32 clocks) -> void;
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
auto main() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto estimateTiming() -> u32;
|
||||
|
||||
//hle.cpp
|
||||
auto mainHLE() -> void;
|
||||
auto addressCRC(u16 address) const -> n5;
|
||||
auto dataCRC(array_view<u8> data) const -> n8;
|
||||
auto run() -> void;
|
||||
auto scan() -> void;
|
||||
auto descramble(n4 *buf, int size) -> void;
|
||||
auto ramReadCommand() -> u8;
|
||||
auto ramWriteCommand(u8 val) -> void;
|
||||
auto memSwap(u32 address, n8 &val) -> void;
|
||||
auto memSwapSecrets() -> void;
|
||||
auto joyInit() -> void;
|
||||
auto joyParse() -> void;
|
||||
auto joyRun() -> void;
|
||||
auto challenge() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto intA(bool dir, bool size) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readInt(u32 address) -> u32;
|
||||
auto writeInt(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto dmaRead(u32 address, u32 ramAddress) -> void;
|
||||
auto dmaWrite(u32 address, u32 ramAddress) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
n1 romLockout;
|
||||
n1 resetEnabled;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
auto PIF::serialize(serializer& s) -> void {
|
||||
s(ram);
|
||||
|
||||
s(state);
|
||||
s(intram);
|
||||
s(io.romLockout);
|
||||
s(io.resetEnabled);
|
||||
}
|
||||
|
||||
auto PIF::Intram::serialize(serializer& s) -> void {
|
||||
s(osInfo);
|
||||
s(cpuChecksum);
|
||||
s(cicChecksum);
|
||||
s(bootTimeout);
|
||||
s(joyAddress);
|
||||
for(auto i: range(5)) s(joyStatus[i].skip), s(joyStatus[i].reset);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto RDP::readWord(u32 address) -> u32 {
|
||||
auto RDP::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -56,7 +56,7 @@ auto RDP::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto RDP::writeWord(u32 address, u32 data_) -> void {
|
||||
auto RDP::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
@ -113,7 +113,7 @@ auto RDP::writeWord(u32 address, u32 data_) -> void {
|
|||
debugger.ioDPC(Write, address, data);
|
||||
}
|
||||
|
||||
auto RDP::IO::readWord(u32 address) -> u32 {
|
||||
auto RDP::IO::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -144,7 +144,7 @@ auto RDP::IO::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto RDP::IO::writeWord(u32 address, u32 data_) -> void {
|
||||
auto RDP::IO::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Reality Display Processor
|
||||
|
||||
struct RDP : Thread, Memory::IO<RDP> {
|
||||
struct RDP : Thread, Memory::RCP<RDP> {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
|
@ -66,8 +66,8 @@ struct RDP : Thread, Memory::IO<RDP> {
|
|||
auto setColorImage() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
auto flushCommands() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
@ -333,13 +333,13 @@ struct RDP : Thread, Memory::IO<RDP> {
|
|||
} x, y;
|
||||
} fillRectangle_;
|
||||
|
||||
struct IO : Memory::IO<IO> {
|
||||
struct IO : Memory::RCP<IO> {
|
||||
RDP& self;
|
||||
IO(RDP& self) : self(self) {}
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
struct BIST {
|
||||
n1 check;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto RDRAM::readWord(u32 address) -> u32 {
|
||||
auto RDRAM::readWord(u32 address, u32& cycles) -> u32 {
|
||||
u32 chipID = address >> 13 & 3;
|
||||
auto& chip = chips[chipID];
|
||||
address = (address & 0x3ff) >> 2;
|
||||
|
@ -63,7 +63,7 @@ auto RDRAM::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto RDRAM::writeWord(u32 address, u32 data) -> void {
|
||||
auto RDRAM::writeWord(u32 address, u32 data, u32& cycles) -> void {
|
||||
u32 chipID = address >> 13 & 3;
|
||||
auto& chip = chips[chipID];
|
||||
address = (address & 0x3ff) >> 2;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//RAMBUS RAM
|
||||
|
||||
struct RDRAM : Memory::IO<RDRAM> {
|
||||
struct RDRAM : Memory::RCP<RDRAM> {
|
||||
Node::Object node;
|
||||
Memory::Writable ram;
|
||||
|
||||
|
@ -24,8 +24,8 @@ struct RDRAM : Memory::IO<RDRAM> {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto RI::readWord(u32 address) -> u32 {
|
||||
auto RI::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = 0;
|
||||
|
||||
|
@ -58,7 +58,7 @@ auto RI::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto RI::writeWord(u32 address, u32 data_) -> void {
|
||||
auto RI::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//RDRAM Interface
|
||||
|
||||
struct RI : Memory::IO<RI> {
|
||||
struct RI : Memory::RCP<RI> {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
|
@ -19,8 +19,8 @@ struct RI : Memory::IO<RI> {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -490,10 +490,10 @@ auto RSP::Disassembler::sccRegisterName(u32 index) const -> string {
|
|||
}
|
||||
|
||||
auto RSP::Disassembler::sccRegisterValue(u32 index) const -> string {
|
||||
u32 value = 0;
|
||||
if(index <= 6) value = rsp.readWord((index & 7) << 2);
|
||||
u32 value = 0; u32 cycles;
|
||||
if(index <= 6) value = rsp.readWord((index & 7) << 2, cycles);
|
||||
if(index == 7) value = self.status.semaphore; //rsp.readSCC(7) has side-effects
|
||||
if(index >= 8) value = rdp.readWord((index & 7) << 2);
|
||||
if(index >= 8) value = rdp.readWord((index & 7) << 2, cycles);
|
||||
if(showValues) return {sccRegisterName(index), hint("{$", hex(value, 8L), "}")};
|
||||
return sccRegisterName(index);
|
||||
}
|
||||
|
@ -525,6 +525,6 @@ auto RSP::Disassembler::ccrRegisterValue(u32 index) const -> string {
|
|||
|
||||
template<typename... P>
|
||||
auto RSP::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
|
||||
if(showColors) return {terminal::csi, "0m", terminal::csi, "37m", std::forward<P>(p)..., terminal::csi, "0m"};
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
auto RSP::MFC0(r32& rt, u8 rd) -> void {
|
||||
u32 cycles = 0;
|
||||
if((rd & 8) == 0) rt.u32 = Nintendo64::rsp.ioRead ((rd & 7) << 2);
|
||||
if((rd & 8) != 0) rt.u32 = Nintendo64::rdp.readWord((rd & 7) << 2);
|
||||
if((rd & 8) != 0) rt.u32 = Nintendo64::rdp.readWord((rd & 7) << 2, cycles);
|
||||
step(cycles);
|
||||
}
|
||||
|
||||
auto RSP::MTC0(cr32& rt, u8 rd) -> void {
|
||||
u32 cycles = 0;
|
||||
if((rd & 8) == 0) Nintendo64::rsp.ioWrite ((rd & 7) << 2, rt.u32);
|
||||
if((rd & 8) != 0) Nintendo64::rdp.writeWord((rd & 7) << 2, rt.u32);
|
||||
if((rd & 8) != 0) Nintendo64::rdp.writeWord((rd & 7) << 2, rt.u32, cycles);
|
||||
step(cycles);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,23 @@
|
|||
#define DIVOUT vpu.divout
|
||||
#define DIVDP vpu.divdp
|
||||
|
||||
static auto countLeadingZeros(u32 value) -> u32 {
|
||||
assert(value);
|
||||
#if defined(COMPILER_MICROSOFT)
|
||||
unsigned long index;
|
||||
_BitScanReverse(&index, value);
|
||||
return index ^ 31;
|
||||
#elif __has_builtin(__builtin_clz)
|
||||
return __builtin_clz(value);
|
||||
#else
|
||||
s32 index;
|
||||
for(index = 31; index >= 0; --index) {
|
||||
if(value >> index & 1) break;
|
||||
}
|
||||
return 31 - index;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RSP::r128::operator()(u32 index) const -> r128 {
|
||||
if constexpr(Accuracy::RSP::SISD) {
|
||||
r128 v{*this};
|
||||
|
@ -60,7 +77,9 @@ auto RSP::r128::operator()(u32 index) const -> r128 {
|
|||
_mm_set_epi8( 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0), //77777777
|
||||
};
|
||||
//todo: benchmark to see if testing for cases 0&1 to return value directly is faster
|
||||
return {uint128_t(_mm_shuffle_epi8(v128, shuffle[index]))};
|
||||
r128 v;
|
||||
v = _mm_shuffle_epi8(v128, shuffle[index]);
|
||||
return v;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -132,8 +151,8 @@ auto RSP::CTC2(cr32& rt, u8 rd) -> void {
|
|||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
static const v128 mask = _mm_set_epi16(0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080);
|
||||
lo->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_shuffle_epi8(r128{~rt.u32 >> 0}, zero), mask), zero);
|
||||
hi->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_shuffle_epi8(r128{~rt.u32 >> 8}, zero), mask), zero);
|
||||
lo->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_set1_epi8(~rt.u32 >> 0), mask), zero);
|
||||
hi->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_set1_epi8(~rt.u32 >> 8), mask), zero);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1321,7 +1340,7 @@ auto RSP::VRCP(r128& vd, u8 de, cr128& vt) -> void {
|
|||
} else if(input == -32768) {
|
||||
result = 0xffff'0000;
|
||||
} else {
|
||||
u32 shift = __builtin_clz(data);
|
||||
u32 shift = countLeadingZeros(data);
|
||||
u32 index = (u64(data) << shift & 0x7fc0'0000) >> 22;
|
||||
result = reciprocals[index];
|
||||
result = (0x10000 | result) << 14;
|
||||
|
@ -1373,7 +1392,7 @@ auto RSP::VRSQ(r128& vd, u8 de, cr128& vt) -> void {
|
|||
} else if(input == -32768) {
|
||||
result = 0xffff'0000;
|
||||
} else {
|
||||
u32 shift = __builtin_clz(data);
|
||||
u32 shift = countLeadingZeros(data);
|
||||
u32 index = (u64(data) << shift & 0x7fc0'0000) >> 22;
|
||||
result = inverseSquareRoots[index & 0x1fe | shift & 1];
|
||||
result = (0x10000 | result) << 14;
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
case 0xd: return name<0xd>(__VA_ARGS__); \
|
||||
case 0xe: return name<0xe>(__VA_ARGS__); \
|
||||
case 0xf: return name<0xf>(__VA_ARGS__); \
|
||||
}
|
||||
} \
|
||||
unreachable;
|
||||
|
||||
#define SA (OP >> 6 & 31)
|
||||
#define RDn (OP >> 11 & 31)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto RSP::readWord(u32 address) -> u32 {
|
||||
auto RSP::readWord(u32 address, u32& cycles) -> u32 {
|
||||
if(address <= 0x0403'ffff) {
|
||||
if(address & 0x1000) return imem.read<Word>(address);
|
||||
else return dmem.read<Word>(address);
|
||||
|
@ -67,7 +67,7 @@ auto RSP::ioRead(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto RSP::writeWord(u32 address, u32 data) -> void {
|
||||
auto RSP::writeWord(u32 address, u32 data, u32& cycles) -> void {
|
||||
if(address <= 0x0403'ffff) {
|
||||
if(address & 0x1000) return recompiler.invalidate(address & 0xfff), imem.write<Word>(address, data);
|
||||
else return dmem.write<Word>(address, data);
|
||||
|
@ -156,7 +156,7 @@ auto RSP::ioWrite(u32 address, u32 data_) -> void {
|
|||
debugger.ioSCC(Write, address, data);
|
||||
}
|
||||
|
||||
auto RSP::Status::readWord(u32 address) -> u32 {
|
||||
auto RSP::Status::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0x7ffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -177,7 +177,7 @@ auto RSP::Status::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto RSP::Status::writeWord(u32 address, u32 data_) -> void {
|
||||
auto RSP::Status::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0x7ffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ auto RSP::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//ADDIU Rt,Rs,i16
|
||||
case 0x08 ... 0x09: {
|
||||
case range2(0x08, 0x09): {
|
||||
add32(mem(Rt), mem(Rs), imm(i16));
|
||||
return 0;
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ auto RSP::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x13 ... 0x1f: {
|
||||
case range13(0x13, 0x1f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ auto RSP::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x2c ... 0x31: {
|
||||
case range6(0x2c, 0x31): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -358,7 +358,7 @@ auto RSP::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x33 ... 0x39: {
|
||||
case range7(0x33, 0x39): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ auto RSP::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x3b ... 0x3f: {
|
||||
case range5(0x3b, 0x3f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -405,8 +405,7 @@ auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
|
||||
//SLLV Rd,Rt,Rs
|
||||
case 0x04: {
|
||||
and32(reg(0), mem(Rs), imm(31));
|
||||
shl32(mem(Rd), mem(Rt), reg(0));
|
||||
mshl32(mem(Rd), mem(Rt), mem(Rs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -417,15 +416,13 @@ auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
|
||||
//SRLV Rd,Rt,Rs
|
||||
case 0x06: {
|
||||
and32(reg(0), mem(Rs), imm(31));
|
||||
lshr32(mem(Rd), mem(Rt), reg(0));
|
||||
mlshr32(mem(Rd), mem(Rt), mem(Rs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//SRAV Rd,Rt,Rs
|
||||
case 0x07: {
|
||||
and32(reg(0), mem(Rs), imm(31));
|
||||
ashr32(mem(Rd), mem(Rt), reg(0));
|
||||
mashr32(mem(Rd), mem(Rt), mem(Rs));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -445,7 +442,7 @@ auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x0a ... 0x0c: {
|
||||
case range3(0x0a, 0x0c): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -456,18 +453,18 @@ auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x0e ... 0x1f: {
|
||||
case range18(0x0e, 0x1f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//ADDU Rd,Rs,Rt
|
||||
case 0x20 ... 0x21: {
|
||||
case range2(0x20, 0x21): {
|
||||
add32(mem(Rd), mem(Rs), mem(Rt));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//SUBU Rd,Rs,Rt
|
||||
case 0x22 ... 0x23: {
|
||||
case range2(0x22, 0x23): {
|
||||
sub32(mem(Rd), mem(Rs), mem(Rt));
|
||||
return 0;
|
||||
}
|
||||
|
@ -493,13 +490,13 @@ auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
//NOR Rd,Rs,Rt
|
||||
case 0x27: {
|
||||
or32(reg(0), mem(Rs), mem(Rt));
|
||||
not32(reg(0), reg(0));
|
||||
xor32(reg(0), reg(0), imm(-1));
|
||||
mov32(mem(Rd), reg(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
case 0x28 ... 0x29: {
|
||||
case range2(0x28, 0x29): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -518,7 +515,7 @@ auto RSP::Recompiler::emitSPECIAL(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x2c ... 0x3f: {
|
||||
case range20(0x2c, 0x3f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -547,7 +544,7 @@ auto RSP::Recompiler::emitREGIMM(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x02 ... 0x0f: {
|
||||
case range14(0x02, 0x0f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -568,7 +565,7 @@ auto RSP::Recompiler::emitREGIMM(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x12 ... 0x1f: {
|
||||
case range14(0x12, 0x1f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -589,7 +586,7 @@ auto RSP::Recompiler::emitSCC(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x01 ... 0x03: {
|
||||
case range3(0x01, 0x03): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -602,7 +599,7 @@ auto RSP::Recompiler::emitSCC(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x05 ... 0x1f: {
|
||||
case range27(0x05, 0x1f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -663,7 +660,7 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x07 ... 0x0f: {
|
||||
case range9(0x07, 0x0f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -871,7 +868,7 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//Broken opcodes: VADDB, VSUBB, VACCB, VSUCB, VSAD, VSAC, VSUM
|
||||
case 0x16 ... 0x1c: {
|
||||
case range7(0x16, 0x1c): {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
|
@ -888,7 +885,7 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//Invalid opcodes
|
||||
case 0x1e ... 0x1f: {
|
||||
case range2(0x1e, 0x1f): {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
|
@ -1023,7 +1020,7 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x2e ... 0x2f: {
|
||||
case range2(0x2e, 0x2f): {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
|
@ -1101,7 +1098,7 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//Broken opcodes: VEXTT, VEXTQ, VEXTN
|
||||
case 0x38 ... 0x3a: {
|
||||
case range3(0x38, 0x3a): {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
|
@ -1119,7 +1116,7 @@ auto RSP::Recompiler::emitVU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//Broken opcodes: VINST, VINSQ, VINSN
|
||||
case 0x3c ... 0x3e: {
|
||||
case range3(0x3c, 0x3e): {
|
||||
lea(reg(1), Vd);
|
||||
lea(reg(2), Vs);
|
||||
lea(reg(3), Vt);
|
||||
|
@ -1250,7 +1247,7 @@ auto RSP::Recompiler::emitLWC2(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x0c ... 0x1f: {
|
||||
case range20(0x0c, 0x1f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1375,7 +1372,7 @@ auto RSP::Recompiler::emitSWC2(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
//INVALID
|
||||
case 0x0c ... 0x1f: {
|
||||
case range20(0x0c, 0x1f): {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,15 +86,15 @@ auto RSP::power(bool reset) -> void {
|
|||
for(auto& r : ipu.r) r.u32 = 0;
|
||||
ipu.pc = 0;
|
||||
branch = {};
|
||||
for(auto& r : vpu.r) r.u128 = 0;
|
||||
vpu.acch.u128 = 0;
|
||||
vpu.accm.u128 = 0;
|
||||
vpu.accl.u128 = 0;
|
||||
vpu.vcoh.u128 = 0;
|
||||
vpu.vcol.u128 = 0;
|
||||
vpu.vcch.u128 = 0;
|
||||
vpu.vccl.u128 = 0;
|
||||
vpu.vce.u128 = 0;
|
||||
for(auto& r : vpu.r) r = zero;
|
||||
vpu.acch = zero;
|
||||
vpu.accm = zero;
|
||||
vpu.accl = zero;
|
||||
vpu.vcoh = zero;
|
||||
vpu.vcol = zero;
|
||||
vpu.vcch = zero;
|
||||
vpu.vccl = zero;
|
||||
vpu.vce = zero;
|
||||
vpu.divin = 0;
|
||||
vpu.divout = 0;
|
||||
vpu.divdp = 0;
|
||||
|
@ -115,8 +115,8 @@ auto RSP::power(bool reset) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::Recompiler) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(4_MiB);
|
||||
recompiler.allocator.resize(4_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
recompiler.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Reality Signal Processor
|
||||
|
||||
struct RSP : Thread, Memory::IO<RSP> {
|
||||
struct RSP : Thread, Memory::RCP<RSP> {
|
||||
Node::Object node;
|
||||
Memory::Writable dmem;
|
||||
Memory::Writable imem;
|
||||
|
@ -47,8 +47,8 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
auto dmaTransferStep() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
auto ioRead(u32 address) -> u32;
|
||||
auto ioWrite(u32 address, u32 data) -> void;
|
||||
|
||||
|
@ -75,13 +75,13 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
} busy, full;
|
||||
} dma;
|
||||
|
||||
struct Status : Memory::IO<Status> {
|
||||
struct Status : Memory::RCP<Status> {
|
||||
RSP& self;
|
||||
Status(RSP& self) : self(self) {}
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
n1 semaphore;
|
||||
n1 halted = 1;
|
||||
|
@ -174,7 +174,7 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
|
||||
//vpu.cpp: Vector Processing Unit
|
||||
union r128 {
|
||||
struct { uint128_t u128; };
|
||||
struct { u64 order_msb2(hi, lo); } u128;
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
struct { __m128i v128; };
|
||||
|
||||
|
@ -203,6 +203,9 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
|
||||
//vu-registers.cpp
|
||||
auto operator()(u32 index) const -> r128;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
};
|
||||
using cr128 = const r128;
|
||||
|
||||
|
@ -217,8 +220,8 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
bool divdp;
|
||||
} vpu;
|
||||
|
||||
static constexpr r128 zero{0};
|
||||
static constexpr r128 invert{u128(0) - 1};
|
||||
static constexpr r128 zero{0ull, 0ull};
|
||||
static constexpr r128 invert{~0ull, ~0ull};
|
||||
|
||||
auto accumulatorGet(u32 index) const -> u64;
|
||||
auto accumulatorSet(u32 index, u64 value) -> void;
|
||||
|
|
|
@ -27,15 +27,15 @@ auto RSP::serialize(serializer& s) -> void {
|
|||
s(branch.pc);
|
||||
s(branch.state);
|
||||
|
||||
for(auto& r : vpu.r) s(r.u128);
|
||||
s(vpu.acch.u128);
|
||||
s(vpu.accm.u128);
|
||||
s(vpu.accl.u128);
|
||||
s(vpu.vcoh.u128);
|
||||
s(vpu.vcol.u128);
|
||||
s(vpu.vcch.u128);
|
||||
s(vpu.vccl.u128);
|
||||
s(vpu.vce.u128);
|
||||
for(auto& r : vpu.r) s(r);
|
||||
s(vpu.acch);
|
||||
s(vpu.accm);
|
||||
s(vpu.accl);
|
||||
s(vpu.vcoh);
|
||||
s(vpu.vcol);
|
||||
s(vpu.vcch);
|
||||
s(vpu.vccl);
|
||||
s(vpu.vce);
|
||||
s(vpu.divin);
|
||||
s(vpu.divout);
|
||||
s(vpu.divdp);
|
||||
|
@ -53,3 +53,8 @@ auto RSP::DMA::Regs::serialize(serializer& s) -> void {
|
|||
s(skip);
|
||||
s(count);
|
||||
}
|
||||
|
||||
auto RSP::r128::serialize(serializer& s) -> void {
|
||||
s(u128.lo);
|
||||
s(u128.hi);
|
||||
}
|
||||
|
|
|
@ -1,21 +1,13 @@
|
|||
auto SI::dmaRead() -> void {
|
||||
pif.run();
|
||||
for(u32 offset = 0; offset < 64; offset += 4) {
|
||||
u32 data = pif.readWord(io.readAddress + offset);
|
||||
rdram.ram.write<Word>(io.dramAddress + offset, data);
|
||||
}
|
||||
pif.dmaRead(io.readAddress, io.dramAddress);
|
||||
io.dmaBusy = 0;
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::SI);
|
||||
}
|
||||
|
||||
auto SI::dmaWrite() -> void {
|
||||
for(u32 offset = 0; offset < 64; offset += 4) {
|
||||
u32 data = rdram.ram.read<Word>(io.dramAddress + offset);
|
||||
pif.writeWord(io.writeAddress + offset, data);
|
||||
}
|
||||
pif.dmaWrite(io.writeAddress, io.dramAddress);
|
||||
io.dmaBusy = 0;
|
||||
io.interrupt = 1;
|
||||
mi.raise(MI::IRQ::SI);
|
||||
pif.run();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
auto SI::readWord(u32 address) -> u32 {
|
||||
auto SI::readWord(u32 address, u32& cycles) -> u32 {
|
||||
if(address <= 0x048f'ffff) return ioRead(address);
|
||||
|
||||
if (unlikely(io.ioBusy)) {
|
||||
writeForceFinish(); //technically, we should wait until Queue::SI_BUS_Write
|
||||
return io.busLatch;
|
||||
}
|
||||
return pif.read<Word>(address);
|
||||
}
|
||||
|
||||
auto SI::ioRead(u32 address) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -44,7 +54,17 @@ auto SI::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto SI::writeWord(u32 address, u32 data_) -> void {
|
||||
auto SI::writeWord(u32 address, u32 data, u32& cycles) -> void {
|
||||
if(address <= 0x048f'ffff) return ioWrite(address, data);
|
||||
|
||||
if(io.ioBusy) return;
|
||||
io.ioBusy = 1;
|
||||
io.busLatch = data;
|
||||
queue.insert(Queue::SI_BUS_Write, 2150*3);
|
||||
return pif.write<Word>(address, data);
|
||||
}
|
||||
|
||||
auto SI::ioWrite(u32 address, u32 data_) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
@ -57,7 +77,8 @@ auto SI::writeWord(u32 address, u32 data_) -> void {
|
|||
//SI_PIF_ADDRESS_READ64B
|
||||
io.readAddress = data.bit(0,31) & ~1;
|
||||
io.dmaBusy = 1;
|
||||
queue.insert(Queue::SI_DMA_Read, 2304);
|
||||
int cycles = pif.estimateTiming();
|
||||
queue.insert(Queue::SI_DMA_Read, cycles*3);
|
||||
}
|
||||
|
||||
if(address == 2) {
|
||||
|
@ -72,7 +93,7 @@ auto SI::writeWord(u32 address, u32 data_) -> void {
|
|||
//SI_PIF_ADDRESS_WRITE64B
|
||||
io.writeAddress = data.bit(0,31) & ~1;
|
||||
io.dmaBusy = 1;
|
||||
queue.insert(Queue::SI_DMA_Write, 2304);
|
||||
queue.insert(Queue::SI_DMA_Write, 4065*3);
|
||||
}
|
||||
|
||||
if(address == 5) {
|
||||
|
@ -87,3 +108,12 @@ auto SI::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
debugger.io(Write, address, data);
|
||||
}
|
||||
|
||||
auto SI::writeFinished() -> void {
|
||||
io.ioBusy = 0;
|
||||
}
|
||||
|
||||
auto SI::writeForceFinish() -> void {
|
||||
io.ioBusy = 0;
|
||||
queue.remove(Queue::SI_BUS_Write);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ auto SI::serialize(serializer& s) -> void {
|
|||
s(io.dramAddress);
|
||||
s(io.readAddress);
|
||||
s(io.writeAddress);
|
||||
s(io.busLatch);
|
||||
s(io.dmaBusy);
|
||||
s(io.ioBusy);
|
||||
s(io.readPending);
|
||||
|
|
|
@ -11,14 +11,6 @@ SI si;
|
|||
auto SI::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("SI");
|
||||
debugger.load(node);
|
||||
|
||||
/*if(auto fp = system.pak->read("pif.sm5.rom")) {
|
||||
//load 1KB ROM and mirror it to 4KB
|
||||
fp->read({SM5K::ROM, 1024});
|
||||
memory::copy(&SM5K::ROM[1024], &SM5K::ROM[0], 1024);
|
||||
memory::copy(&SM5K::ROM[2048], &SM5K::ROM[0], 1024);
|
||||
memory::copy(&SM5K::ROM[3072], &SM5K::ROM[0], 1024);
|
||||
}*/
|
||||
}
|
||||
|
||||
auto SI::unload() -> void {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Serial Interface
|
||||
|
||||
struct SI : Memory::IO<SI> {
|
||||
struct SI : Memory::RCP<SI> {
|
||||
Node::Object node;
|
||||
|
||||
struct Debugger {
|
||||
|
@ -23,8 +23,12 @@ struct SI : Memory::IO<SI> {
|
|||
auto dmaWrite() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto ioRead(u32 address) -> u32;
|
||||
auto ioWrite(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
auto writeFinished() -> void;
|
||||
auto writeForceFinish() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
@ -33,6 +37,7 @@ struct SI : Memory::IO<SI> {
|
|||
n24 dramAddress;
|
||||
n32 readAddress;
|
||||
n32 writeAddress;
|
||||
u32 busLatch;
|
||||
n1 dmaBusy;
|
||||
n1 ioBusy;
|
||||
n1 readPending;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
static const string SerializerVersion = "v131";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
serializer s;
|
||||
|
||||
|
@ -47,6 +49,7 @@ auto System::serialize(serializer& s, bool synchronize) -> void {
|
|||
s(ai);
|
||||
s(pi);
|
||||
s(pif);
|
||||
s(cic);
|
||||
s(ri);
|
||||
s(si);
|
||||
s(cpu);
|
||||
|
|
|
@ -40,7 +40,6 @@ auto System::game() -> string {
|
|||
auto System::run() -> void {
|
||||
while(!vi.refreshed) cpu.main();
|
||||
vi.refreshed = false;
|
||||
if (!pif.io.romLockout) pif.run();
|
||||
}
|
||||
|
||||
auto System::load(Node::System& root, string name) -> bool {
|
||||
|
@ -120,7 +119,7 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::save() -> void {
|
||||
/*
|
||||
#if false
|
||||
if(!node) return;
|
||||
cartridge.save();
|
||||
controllerPort1.save();
|
||||
|
@ -128,7 +127,7 @@ auto System::save() -> void {
|
|||
controllerPort3.save();
|
||||
controllerPort4.save();
|
||||
if(_DD()) dd.save();
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
auto System::power(bool reset) -> void {
|
||||
|
@ -146,6 +145,7 @@ auto System::power(bool reset) -> void {
|
|||
ai.power(reset);
|
||||
pi.power(reset);
|
||||
pif.power(reset);
|
||||
cic.power(reset);
|
||||
ri.power(reset);
|
||||
si.power(reset);
|
||||
cpu.power(reset);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
auto VI::readWord(u32 address) -> u32 {
|
||||
auto VI::readWord(u32 address, u32& cycles) -> u32 {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data;
|
||||
|
||||
|
@ -96,7 +96,7 @@ auto VI::readWord(u32 address) -> u32 {
|
|||
return data;
|
||||
}
|
||||
|
||||
auto VI::writeWord(u32 address, u32 data_) -> void {
|
||||
auto VI::writeWord(u32 address, u32 data_, u32& cycles) -> void {
|
||||
address = (address & 0xfffff) >> 2;
|
||||
n32 data = data_;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Video Interface
|
||||
|
||||
struct VI : Thread, Memory::IO<VI> {
|
||||
struct VI : Thread, Memory::RCP<VI> {
|
||||
Node::Object node;
|
||||
Node::Video::Screen screen;
|
||||
|
||||
|
@ -24,8 +24,8 @@ struct VI : Thread, Memory::IO<VI> {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
auto readWord(u32 address, u32& cycles) -> u32;
|
||||
auto writeWord(u32 address, u32 data, u32& cycles) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/directory.hpp>
|
||||
#include <nall/file.hpp>
|
||||
#include <nall/location.hpp>
|
||||
#include <nall/path.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct BCD {
|
||||
static auto encode(u8 value) -> u8 { return value / 10 << 4 | value % 10; }
|
||||
static auto decode(u8 value) -> u8 { return (value >> 4) * 10 + (value & 15); }
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
#pragma once
|
||||
|
||||
//GNU case range extension simulated with preprocessor macros
|
||||
//
|
||||
// usage expands to
|
||||
// ------------------ -----------------------------------
|
||||
// caseN(X): => case X:
|
||||
// case X + 1:
|
||||
// ...
|
||||
// case X + N - 1:
|
||||
//
|
||||
// case rangeN(X, Y): => case X:
|
||||
// static_assert(Y - X + 1 == N);
|
||||
// case X + 1:
|
||||
// ...
|
||||
// case Y:
|
||||
//
|
||||
//the range macro more closely resembles the GNU extension syntax at the cost of
|
||||
//redundancy. it embeds a static assert between the first two case labels to
|
||||
//ensure consistency between the chosen macro and the provided macro arguments.
|
||||
|
||||
#define check_case_range(x, y, n) static_assert((y) - (x) + 1 == n, "range does not contain " #n " elements"); [[fallthrough]];
|
||||
|
||||
#define case1(x) case (x)
|
||||
#define case2(x) case1(x): case1((x) + 1)
|
||||
#define case3(x) case1(x): case2((x) + 1)
|
||||
#define case4(x) case2(x): case2((x) + 2)
|
||||
#define case5(x) case2(x): case3((x) + 2)
|
||||
#define case6(x) case3(x): case3((x) + 3)
|
||||
#define case7(x) case3(x): case4((x) + 3)
|
||||
#define case8(x) case4(x): case4((x) + 4)
|
||||
#define case9(x) case4(x): case5((x) + 4)
|
||||
#define case10(x) case5(x): case5((x) + 5)
|
||||
#define case11(x) case5(x): case6((x) + 5)
|
||||
#define case12(x) case6(x): case6((x) + 6)
|
||||
#define case13(x) case6(x): case7((x) + 6)
|
||||
#define case14(x) case7(x): case7((x) + 7)
|
||||
#define case15(x) case7(x): case8((x) + 7)
|
||||
#define case16(x) case8(x): case8((x) + 8)
|
||||
#define case17(x) case8(x): case9((x) + 8)
|
||||
#define case18(x) case9(x): case9((x) + 9)
|
||||
#define case19(x) case9(x): case10((x) + 9)
|
||||
#define case20(x) case10(x): case10((x) + 10)
|
||||
#define case21(x) case10(x): case11((x) + 10)
|
||||
#define case22(x) case11(x): case11((x) + 11)
|
||||
#define case23(x) case11(x): case12((x) + 11)
|
||||
#define case24(x) case12(x): case12((x) + 12)
|
||||
#define case25(x) case12(x): case13((x) + 12)
|
||||
#define case26(x) case13(x): case13((x) + 13)
|
||||
#define case27(x) case13(x): case14((x) + 13)
|
||||
#define case28(x) case14(x): case14((x) + 14)
|
||||
#define case29(x) case14(x): case15((x) + 14)
|
||||
#define case30(x) case15(x): case15((x) + 15)
|
||||
#define case31(x) case15(x): case16((x) + 15)
|
||||
#define case32(x) case16(x): case16((x) + 16)
|
||||
#define case33(x) case16(x): case17((x) + 16)
|
||||
#define case34(x) case17(x): case17((x) + 17)
|
||||
#define case35(x) case17(x): case18((x) + 17)
|
||||
#define case36(x) case18(x): case18((x) + 18)
|
||||
#define case37(x) case18(x): case19((x) + 18)
|
||||
#define case38(x) case19(x): case19((x) + 19)
|
||||
#define case39(x) case19(x): case20((x) + 19)
|
||||
#define case40(x) case20(x): case20((x) + 20)
|
||||
#define case41(x) case20(x): case21((x) + 20)
|
||||
#define case42(x) case21(x): case21((x) + 21)
|
||||
#define case43(x) case21(x): case22((x) + 21)
|
||||
#define case44(x) case22(x): case22((x) + 22)
|
||||
#define case45(x) case22(x): case23((x) + 22)
|
||||
#define case46(x) case23(x): case23((x) + 23)
|
||||
#define case47(x) case23(x): case24((x) + 23)
|
||||
#define case48(x) case24(x): case24((x) + 24)
|
||||
#define case49(x) case24(x): case25((x) + 24)
|
||||
#define case50(x) case25(x): case25((x) + 25)
|
||||
#define case51(x) case25(x): case26((x) + 25)
|
||||
#define case52(x) case26(x): case26((x) + 26)
|
||||
#define case53(x) case26(x): case27((x) + 26)
|
||||
#define case54(x) case27(x): case27((x) + 27)
|
||||
#define case55(x) case27(x): case28((x) + 27)
|
||||
#define case56(x) case28(x): case28((x) + 28)
|
||||
#define case57(x) case28(x): case29((x) + 28)
|
||||
#define case58(x) case29(x): case29((x) + 29)
|
||||
#define case59(x) case29(x): case30((x) + 29)
|
||||
#define case60(x) case30(x): case30((x) + 30)
|
||||
#define case61(x) case30(x): case31((x) + 30)
|
||||
#define case62(x) case31(x): case31((x) + 31)
|
||||
#define case63(x) case31(x): case32((x) + 31)
|
||||
#define case64(x) case32(x): case32((x) + 32)
|
||||
#define case65(x) case32(x): case33((x) + 32)
|
||||
#define case66(x) case33(x): case33((x) + 33)
|
||||
#define case67(x) case33(x): case34((x) + 33)
|
||||
#define case68(x) case34(x): case34((x) + 34)
|
||||
#define case69(x) case34(x): case35((x) + 34)
|
||||
#define case70(x) case35(x): case35((x) + 35)
|
||||
#define case71(x) case35(x): case36((x) + 35)
|
||||
#define case72(x) case36(x): case36((x) + 36)
|
||||
#define case73(x) case36(x): case37((x) + 36)
|
||||
#define case74(x) case37(x): case37((x) + 37)
|
||||
#define case75(x) case37(x): case38((x) + 37)
|
||||
#define case76(x) case38(x): case38((x) + 38)
|
||||
#define case77(x) case38(x): case39((x) + 38)
|
||||
#define case78(x) case39(x): case39((x) + 39)
|
||||
#define case79(x) case39(x): case40((x) + 39)
|
||||
#define case80(x) case40(x): case40((x) + 40)
|
||||
#define case81(x) case40(x): case41((x) + 40)
|
||||
#define case82(x) case41(x): case41((x) + 41)
|
||||
#define case83(x) case41(x): case42((x) + 41)
|
||||
#define case84(x) case42(x): case42((x) + 42)
|
||||
#define case85(x) case42(x): case43((x) + 42)
|
||||
#define case86(x) case43(x): case43((x) + 43)
|
||||
#define case87(x) case43(x): case44((x) + 43)
|
||||
#define case88(x) case44(x): case44((x) + 44)
|
||||
#define case89(x) case44(x): case45((x) + 44)
|
||||
#define case90(x) case45(x): case45((x) + 45)
|
||||
#define case91(x) case45(x): case46((x) + 45)
|
||||
#define case92(x) case46(x): case46((x) + 46)
|
||||
#define case93(x) case46(x): case47((x) + 46)
|
||||
#define case94(x) case47(x): case47((x) + 47)
|
||||
#define case95(x) case47(x): case48((x) + 47)
|
||||
#define case96(x) case48(x): case48((x) + 48)
|
||||
#define case97(x) case48(x): case49((x) + 48)
|
||||
#define case98(x) case49(x): case49((x) + 49)
|
||||
#define case99(x) case49(x): case50((x) + 49)
|
||||
#define case100(x) case50(x): case50((x) + 50)
|
||||
#define case101(x) case50(x): case51((x) + 50)
|
||||
#define case102(x) case51(x): case51((x) + 51)
|
||||
#define case103(x) case51(x): case52((x) + 51)
|
||||
#define case104(x) case52(x): case52((x) + 52)
|
||||
#define case105(x) case52(x): case53((x) + 52)
|
||||
#define case106(x) case53(x): case53((x) + 53)
|
||||
#define case107(x) case53(x): case54((x) + 53)
|
||||
#define case108(x) case54(x): case54((x) + 54)
|
||||
#define case109(x) case54(x): case55((x) + 54)
|
||||
#define case110(x) case55(x): case55((x) + 55)
|
||||
#define case111(x) case55(x): case56((x) + 55)
|
||||
#define case112(x) case56(x): case56((x) + 56)
|
||||
#define case113(x) case56(x): case57((x) + 56)
|
||||
#define case114(x) case57(x): case57((x) + 57)
|
||||
#define case115(x) case57(x): case58((x) + 57)
|
||||
#define case116(x) case58(x): case58((x) + 58)
|
||||
#define case117(x) case58(x): case59((x) + 58)
|
||||
#define case118(x) case59(x): case59((x) + 59)
|
||||
#define case119(x) case59(x): case60((x) + 59)
|
||||
#define case120(x) case60(x): case60((x) + 60)
|
||||
#define case121(x) case60(x): case61((x) + 60)
|
||||
#define case122(x) case61(x): case61((x) + 61)
|
||||
#define case123(x) case61(x): case62((x) + 61)
|
||||
#define case124(x) case62(x): case62((x) + 62)
|
||||
#define case125(x) case62(x): case63((x) + 62)
|
||||
#define case126(x) case63(x): case63((x) + 63)
|
||||
#define case127(x) case63(x): case64((x) + 63)
|
||||
#define case128(x) case64(x): case64((x) + 64)
|
||||
|
||||
#define range2(x, y) (x): check_case_range(x, y, 2) case1((x) + 1)
|
||||
#define range3(x, y) (x): check_case_range(x, y, 3) case2((x) + 1)
|
||||
#define range4(x, y) (x): check_case_range(x, y, 4) case3((x) + 1)
|
||||
#define range5(x, y) (x): check_case_range(x, y, 5) case4((x) + 1)
|
||||
#define range6(x, y) (x): check_case_range(x, y, 6) case5((x) + 1)
|
||||
#define range7(x, y) (x): check_case_range(x, y, 7) case6((x) + 1)
|
||||
#define range8(x, y) (x): check_case_range(x, y, 8) case7((x) + 1)
|
||||
#define range9(x, y) (x): check_case_range(x, y, 9) case8((x) + 1)
|
||||
#define range10(x, y) (x): check_case_range(x, y, 10) case9((x) + 1)
|
||||
#define range11(x, y) (x): check_case_range(x, y, 11) case10((x) + 1)
|
||||
#define range12(x, y) (x): check_case_range(x, y, 12) case11((x) + 1)
|
||||
#define range13(x, y) (x): check_case_range(x, y, 13) case12((x) + 1)
|
||||
#define range14(x, y) (x): check_case_range(x, y, 14) case13((x) + 1)
|
||||
#define range15(x, y) (x): check_case_range(x, y, 15) case14((x) + 1)
|
||||
#define range16(x, y) (x): check_case_range(x, y, 16) case15((x) + 1)
|
||||
#define range17(x, y) (x): check_case_range(x, y, 17) case16((x) + 1)
|
||||
#define range18(x, y) (x): check_case_range(x, y, 18) case17((x) + 1)
|
||||
#define range19(x, y) (x): check_case_range(x, y, 19) case18((x) + 1)
|
||||
#define range20(x, y) (x): check_case_range(x, y, 20) case19((x) + 1)
|
||||
#define range21(x, y) (x): check_case_range(x, y, 21) case20((x) + 1)
|
||||
#define range22(x, y) (x): check_case_range(x, y, 22) case21((x) + 1)
|
||||
#define range23(x, y) (x): check_case_range(x, y, 23) case22((x) + 1)
|
||||
#define range24(x, y) (x): check_case_range(x, y, 24) case23((x) + 1)
|
||||
#define range25(x, y) (x): check_case_range(x, y, 25) case24((x) + 1)
|
||||
#define range26(x, y) (x): check_case_range(x, y, 26) case25((x) + 1)
|
||||
#define range27(x, y) (x): check_case_range(x, y, 27) case26((x) + 1)
|
||||
#define range28(x, y) (x): check_case_range(x, y, 28) case27((x) + 1)
|
||||
#define range29(x, y) (x): check_case_range(x, y, 29) case28((x) + 1)
|
||||
#define range30(x, y) (x): check_case_range(x, y, 30) case29((x) + 1)
|
||||
#define range31(x, y) (x): check_case_range(x, y, 31) case30((x) + 1)
|
||||
#define range32(x, y) (x): check_case_range(x, y, 32) case31((x) + 1)
|
||||
#define range33(x, y) (x): check_case_range(x, y, 33) case32((x) + 1)
|
||||
#define range34(x, y) (x): check_case_range(x, y, 34) case33((x) + 1)
|
||||
#define range35(x, y) (x): check_case_range(x, y, 35) case34((x) + 1)
|
||||
#define range36(x, y) (x): check_case_range(x, y, 36) case35((x) + 1)
|
||||
#define range37(x, y) (x): check_case_range(x, y, 37) case36((x) + 1)
|
||||
#define range38(x, y) (x): check_case_range(x, y, 38) case37((x) + 1)
|
||||
#define range39(x, y) (x): check_case_range(x, y, 39) case38((x) + 1)
|
||||
#define range40(x, y) (x): check_case_range(x, y, 40) case39((x) + 1)
|
||||
#define range41(x, y) (x): check_case_range(x, y, 41) case40((x) + 1)
|
||||
#define range42(x, y) (x): check_case_range(x, y, 42) case41((x) + 1)
|
||||
#define range43(x, y) (x): check_case_range(x, y, 43) case42((x) + 1)
|
||||
#define range44(x, y) (x): check_case_range(x, y, 44) case43((x) + 1)
|
||||
#define range45(x, y) (x): check_case_range(x, y, 45) case44((x) + 1)
|
||||
#define range46(x, y) (x): check_case_range(x, y, 46) case45((x) + 1)
|
||||
#define range47(x, y) (x): check_case_range(x, y, 47) case46((x) + 1)
|
||||
#define range48(x, y) (x): check_case_range(x, y, 48) case47((x) + 1)
|
||||
#define range49(x, y) (x): check_case_range(x, y, 49) case48((x) + 1)
|
||||
#define range50(x, y) (x): check_case_range(x, y, 50) case49((x) + 1)
|
||||
#define range51(x, y) (x): check_case_range(x, y, 51) case50((x) + 1)
|
||||
#define range52(x, y) (x): check_case_range(x, y, 52) case51((x) + 1)
|
||||
#define range53(x, y) (x): check_case_range(x, y, 53) case52((x) + 1)
|
||||
#define range54(x, y) (x): check_case_range(x, y, 54) case53((x) + 1)
|
||||
#define range55(x, y) (x): check_case_range(x, y, 55) case54((x) + 1)
|
||||
#define range56(x, y) (x): check_case_range(x, y, 56) case55((x) + 1)
|
||||
#define range57(x, y) (x): check_case_range(x, y, 57) case56((x) + 1)
|
||||
#define range58(x, y) (x): check_case_range(x, y, 58) case57((x) + 1)
|
||||
#define range59(x, y) (x): check_case_range(x, y, 59) case58((x) + 1)
|
||||
#define range60(x, y) (x): check_case_range(x, y, 60) case59((x) + 1)
|
||||
#define range61(x, y) (x): check_case_range(x, y, 61) case60((x) + 1)
|
||||
#define range62(x, y) (x): check_case_range(x, y, 62) case61((x) + 1)
|
||||
#define range63(x, y) (x): check_case_range(x, y, 63) case62((x) + 1)
|
||||
#define range64(x, y) (x): check_case_range(x, y, 64) case63((x) + 1)
|
||||
#define range65(x, y) (x): check_case_range(x, y, 65) case64((x) + 1)
|
||||
#define range66(x, y) (x): check_case_range(x, y, 66) case65((x) + 1)
|
||||
#define range67(x, y) (x): check_case_range(x, y, 67) case66((x) + 1)
|
||||
#define range68(x, y) (x): check_case_range(x, y, 68) case67((x) + 1)
|
||||
#define range69(x, y) (x): check_case_range(x, y, 69) case68((x) + 1)
|
||||
#define range70(x, y) (x): check_case_range(x, y, 70) case69((x) + 1)
|
||||
#define range71(x, y) (x): check_case_range(x, y, 71) case70((x) + 1)
|
||||
#define range72(x, y) (x): check_case_range(x, y, 72) case71((x) + 1)
|
||||
#define range73(x, y) (x): check_case_range(x, y, 73) case72((x) + 1)
|
||||
#define range74(x, y) (x): check_case_range(x, y, 74) case73((x) + 1)
|
||||
#define range75(x, y) (x): check_case_range(x, y, 75) case74((x) + 1)
|
||||
#define range76(x, y) (x): check_case_range(x, y, 76) case75((x) + 1)
|
||||
#define range77(x, y) (x): check_case_range(x, y, 77) case76((x) + 1)
|
||||
#define range78(x, y) (x): check_case_range(x, y, 78) case77((x) + 1)
|
||||
#define range79(x, y) (x): check_case_range(x, y, 79) case78((x) + 1)
|
||||
#define range80(x, y) (x): check_case_range(x, y, 80) case79((x) + 1)
|
||||
#define range81(x, y) (x): check_case_range(x, y, 81) case80((x) + 1)
|
||||
#define range82(x, y) (x): check_case_range(x, y, 82) case81((x) + 1)
|
||||
#define range83(x, y) (x): check_case_range(x, y, 83) case82((x) + 1)
|
||||
#define range84(x, y) (x): check_case_range(x, y, 84) case83((x) + 1)
|
||||
#define range85(x, y) (x): check_case_range(x, y, 85) case84((x) + 1)
|
||||
#define range86(x, y) (x): check_case_range(x, y, 86) case85((x) + 1)
|
||||
#define range87(x, y) (x): check_case_range(x, y, 87) case86((x) + 1)
|
||||
#define range88(x, y) (x): check_case_range(x, y, 88) case87((x) + 1)
|
||||
#define range89(x, y) (x): check_case_range(x, y, 89) case88((x) + 1)
|
||||
#define range90(x, y) (x): check_case_range(x, y, 90) case89((x) + 1)
|
||||
#define range91(x, y) (x): check_case_range(x, y, 91) case90((x) + 1)
|
||||
#define range92(x, y) (x): check_case_range(x, y, 92) case91((x) + 1)
|
||||
#define range93(x, y) (x): check_case_range(x, y, 93) case92((x) + 1)
|
||||
#define range94(x, y) (x): check_case_range(x, y, 94) case93((x) + 1)
|
||||
#define range95(x, y) (x): check_case_range(x, y, 95) case94((x) + 1)
|
||||
#define range96(x, y) (x): check_case_range(x, y, 96) case95((x) + 1)
|
||||
#define range97(x, y) (x): check_case_range(x, y, 97) case96((x) + 1)
|
||||
#define range98(x, y) (x): check_case_range(x, y, 98) case97((x) + 1)
|
||||
#define range99(x, y) (x): check_case_range(x, y, 99) case98((x) + 1)
|
||||
#define range100(x, y) (x): check_case_range(x, y, 100) case99((x) + 1)
|
||||
#define range101(x, y) (x): check_case_range(x, y, 101) case100((x) + 1)
|
||||
#define range102(x, y) (x): check_case_range(x, y, 102) case101((x) + 1)
|
||||
#define range103(x, y) (x): check_case_range(x, y, 103) case102((x) + 1)
|
||||
#define range104(x, y) (x): check_case_range(x, y, 104) case103((x) + 1)
|
||||
#define range105(x, y) (x): check_case_range(x, y, 105) case104((x) + 1)
|
||||
#define range106(x, y) (x): check_case_range(x, y, 106) case105((x) + 1)
|
||||
#define range107(x, y) (x): check_case_range(x, y, 107) case106((x) + 1)
|
||||
#define range108(x, y) (x): check_case_range(x, y, 108) case107((x) + 1)
|
||||
#define range109(x, y) (x): check_case_range(x, y, 109) case108((x) + 1)
|
||||
#define range110(x, y) (x): check_case_range(x, y, 110) case109((x) + 1)
|
||||
#define range111(x, y) (x): check_case_range(x, y, 111) case110((x) + 1)
|
||||
#define range112(x, y) (x): check_case_range(x, y, 112) case111((x) + 1)
|
||||
#define range113(x, y) (x): check_case_range(x, y, 113) case112((x) + 1)
|
||||
#define range114(x, y) (x): check_case_range(x, y, 114) case113((x) + 1)
|
||||
#define range115(x, y) (x): check_case_range(x, y, 115) case114((x) + 1)
|
||||
#define range116(x, y) (x): check_case_range(x, y, 116) case115((x) + 1)
|
||||
#define range117(x, y) (x): check_case_range(x, y, 117) case116((x) + 1)
|
||||
#define range118(x, y) (x): check_case_range(x, y, 118) case117((x) + 1)
|
||||
#define range119(x, y) (x): check_case_range(x, y, 119) case118((x) + 1)
|
||||
#define range120(x, y) (x): check_case_range(x, y, 120) case119((x) + 1)
|
||||
#define range121(x, y) (x): check_case_range(x, y, 121) case120((x) + 1)
|
||||
#define range122(x, y) (x): check_case_range(x, y, 122) case121((x) + 1)
|
||||
#define range123(x, y) (x): check_case_range(x, y, 123) case122((x) + 1)
|
||||
#define range124(x, y) (x): check_case_range(x, y, 124) case123((x) + 1)
|
||||
#define range125(x, y) (x): check_case_range(x, y, 125) case124((x) + 1)
|
||||
#define range126(x, y) (x): check_case_range(x, y, 126) case125((x) + 1)
|
||||
#define range127(x, y) (x): check_case_range(x, y, 127) case126((x) + 1)
|
||||
#define range128(x, y) (x): check_case_range(x, y, 128) case127((x) + 1)
|
|
@ -22,6 +22,7 @@
|
|||
#include <nall/matrix.hpp>
|
||||
#include <nall/reed-solomon.hpp>
|
||||
|
||||
#include <nall/bcd.hpp>
|
||||
#include <nall/cd/crc16.hpp>
|
||||
#include <nall/cd/efm.hpp>
|
||||
#include <nall/cd/sync.hpp>
|
||||
|
|
|
@ -7,11 +7,6 @@ namespace nall::CD {
|
|||
|
||||
enum : s32 { InvalidLBA = 100 * 60 * 75 };
|
||||
|
||||
struct BCD {
|
||||
static auto encode(u8 value) -> u8 { return value / 10 << 4 | value % 10; }
|
||||
static auto decode(u8 value) -> u8 { return (value >> 4) * 10 + (value & 15); }
|
||||
};
|
||||
|
||||
struct MSF {
|
||||
u8 minute; //00-99
|
||||
u8 second; //00-59
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
#include <nall/function.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace nall::chrono {
|
||||
|
||||
//passage of time functions (from unknown epoch)
|
||||
|
||||
inline auto nanosecond() -> u64 {
|
||||
timespec tv;
|
||||
clock_gettime(CLOCK_MONOTONIC, &tv);
|
||||
return tv.tv_sec * 1'000'000'000 + tv.tv_nsec;
|
||||
auto now = std::chrono::steady_clock::now().time_since_epoch();
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
|
||||
}
|
||||
|
||||
inline auto microsecond() -> u64 { return nanosecond() / 1'000; }
|
||||
|
|
|
@ -102,7 +102,7 @@ struct HChaCha20 : protected ChaCha20 {
|
|||
//192-bit nonce; 64-bit x 64-byte (256GB) counter
|
||||
struct XChaCha20 : ChaCha20 {
|
||||
XChaCha20(u256 key, u192 nonce, u64 counter = 0):
|
||||
ChaCha20(HChaCha20(key, nonce).key(), nonce >> 128, counter) {
|
||||
ChaCha20(HChaCha20(key, u128(nonce)).key(), nonce >> 128, counter) {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -116,9 +116,9 @@ struct SQLite3 {
|
|||
auto& bind(u32 column, u64 value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, intmax value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, uintmax value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, nall::boolean value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, nall::integer value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, nall::natural value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, nall::Boolean value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, nall::Integer value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, nall::Natural value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, f64 value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(u32 column, const nall::string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||
auto& bind(u32 column, const vector<u8>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||
|
@ -131,9 +131,9 @@ struct SQLite3 {
|
|||
auto& bind(u64 value) { return bind(_input++, value); }
|
||||
auto& bind(intmax value) { return bind(_input++, value); }
|
||||
auto& bind(uintmax value) { return bind(_input++, value); }
|
||||
auto& bind(nall::boolean value) { return bind(_input++, value); }
|
||||
auto& bind(nall::integer value) { return bind(_input++, value); }
|
||||
auto& bind(nall::natural value) { return bind(_input++, value); }
|
||||
auto& bind(nall::Boolean value) { return bind(_input++, value); }
|
||||
auto& bind(nall::Integer value) { return bind(_input++, value); }
|
||||
auto& bind(nall::Natural value) { return bind(_input++, value); }
|
||||
auto& bind(f64 value) { return bind(_input++, value); }
|
||||
auto& bind(const nall::string& value) { return bind(_input++, value); }
|
||||
auto& bind(const vector<u8>& value) { return bind(_input++, value); }
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
#include <nall/file.hpp>
|
||||
#include <nall/maybe.hpp>
|
||||
#include <nall/string.hpp>
|
||||
//#include <libchdr/chd.h>
|
||||
#if false
|
||||
#include <libchdr/chd.h>
|
||||
#endif
|
||||
|
||||
namespace nall::Decode {
|
||||
|
||||
|
@ -35,7 +37,9 @@ struct CHD {
|
|||
vector<Track> tracks;
|
||||
private:
|
||||
file_buffer fp;
|
||||
//chd_file* chd = nullptr;
|
||||
#if false
|
||||
chd_file* chd = nullptr;
|
||||
#endif
|
||||
const int chd_sector_size = 2352 + 96;
|
||||
size_t chd_hunk_size;
|
||||
vector<u8> chd_hunk_buffer;
|
||||
|
@ -43,9 +47,11 @@ private:
|
|||
};
|
||||
|
||||
inline CHD::~CHD() {
|
||||
/*if (chd != nullptr) {
|
||||
#if false
|
||||
if (chd != nullptr) {
|
||||
chd_close(chd);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline auto CHD::load(const string& location) -> bool {
|
||||
|
@ -56,8 +62,8 @@ inline auto CHD::load(const string& location) -> bool {
|
|||
}
|
||||
|
||||
return false;
|
||||
|
||||
/*chd_error err = chd_open_file(fp.handle(), CHD_OPEN_READ, nullptr, &chd);
|
||||
#if false
|
||||
chd_error err = chd_open_file(fp.handle(), CHD_OPEN_READ, nullptr, &chd);
|
||||
if (err != CHDERR_NONE) {
|
||||
print("CHD: Failed to open ", location, ": ", chd_error_string(err), "\n");
|
||||
return false;
|
||||
|
@ -112,7 +118,7 @@ inline auto CHD::load(const string& location) -> bool {
|
|||
|
||||
// We currently only support RAW and audio tracks; log an error and exit if we see anything different
|
||||
auto typeStr = string{type};
|
||||
if (!(typeStr.find("_RAW") || typeStr.find("AUDIO"))) {
|
||||
if (!(typeStr.find("_RAW") || typeStr.find("AUDIO") || typeStr.find("MODE1"))) {
|
||||
print("CHD: Unsupported track type: ", type, "\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -180,18 +186,20 @@ inline auto CHD::load(const string& location) -> bool {
|
|||
tracks.append(track);
|
||||
}
|
||||
|
||||
return true;*/
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline auto CHD::read(u32 sector) -> vector<u8> {
|
||||
// Convert LBA in CD-ROM to LBA in CHD
|
||||
/*for(auto& track : tracks) {
|
||||
#if false
|
||||
for(auto& track : tracks) {
|
||||
for(auto& index : track.indices) {
|
||||
if (sector >= index.lba && sector <= index.end) {
|
||||
auto chd_lba = (sector - index.lba) + index.chd_lba;
|
||||
|
||||
vector<u8> output;
|
||||
output.resize(2352);
|
||||
output.resize(track.type == "MODE1" ? 2048 : 2352);
|
||||
|
||||
int hunk = (chd_lba * chd_sector_size) / chd_hunk_size;
|
||||
int offset = (chd_lba * chd_sector_size) % chd_hunk_size;
|
||||
|
@ -215,7 +223,7 @@ inline auto CHD::read(u32 sector) -> vector<u8> {
|
|||
dst_ptr += sizeof(value);
|
||||
}
|
||||
} else {
|
||||
std::copy(chd_hunk_buffer.data() + offset, chd_hunk_buffer.data() + offset + 2352, output.data());
|
||||
std::copy(chd_hunk_buffer.data() + offset, chd_hunk_buffer.data() + offset + output.size(), output.data());
|
||||
}
|
||||
|
||||
return output;
|
||||
|
@ -223,7 +231,8 @@ inline auto CHD::read(u32 sector) -> vector<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
print("CHD: Attempting to read from unmapped sector ", sector, "\n");*/
|
||||
print("CHD: Attempting to read from unmapped sector ", sector, "\n");
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#include <nall/directory.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
NALL_HEADER_INLINE auto directory::exists(const string& pathname) -> bool {
|
||||
if(!pathname) return false;
|
||||
string name = pathname;
|
||||
name.trim("\"", "\"");
|
||||
DWORD result = GetFileAttributes(utf16_t(name));
|
||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||
return (result & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
NALL_HEADER_INLINE auto directory::ufolders(const string& pathname, const string& pattern) -> vector<string> {
|
||||
if(!pathname) {
|
||||
//special root pseudo-folder (return list of drives)
|
||||
wchar_t drives[PATH_MAX] = {0};
|
||||
GetLogicalDriveStrings(PATH_MAX, drives);
|
||||
wchar_t* p = drives;
|
||||
while(*p || *(p + 1)) {
|
||||
if(!*p) *p = ';';
|
||||
p++;
|
||||
}
|
||||
return string{(const char*)utf8_t(drives)}.replace("\\", "/").split(";");
|
||||
}
|
||||
|
||||
vector<string> list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
if(!path.endsWith("\\")) path.append("\\");
|
||||
path.append("*");
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA data;
|
||||
handle = FindFirstFile(utf16_t(path), &data);
|
||||
if(handle != INVALID_HANDLE_VALUE) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
NALL_HEADER_INLINE auto directory::ufiles(const string& pathname, const string& pattern) -> vector<string> {
|
||||
if(!pathname) return {};
|
||||
|
||||
vector<string> list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
if(!path.endsWith("\\")) path.append("\\");
|
||||
path.append("*");
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA data;
|
||||
handle = FindFirstFile(utf16_t(path), &data);
|
||||
if(handle != INVALID_HANDLE_VALUE) {
|
||||
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -192,83 +192,6 @@ inline auto directory::copy(const string& source, const string& target) -> bool
|
|||
}
|
||||
return _wrmdir(utf16_t(pathname)) == 0;
|
||||
}
|
||||
|
||||
inline auto directory::exists(const string& pathname) -> bool {
|
||||
if(!pathname) return false;
|
||||
string name = pathname;
|
||||
name.trim("\"", "\"");
|
||||
DWORD result = GetFileAttributes(utf16_t(name));
|
||||
if(result == INVALID_FILE_ATTRIBUTES) return false;
|
||||
return (result & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
inline auto directory::ufolders(const string& pathname, const string& pattern) -> vector<string> {
|
||||
if(!pathname) {
|
||||
//special root pseudo-folder (return list of drives)
|
||||
wchar_t drives[PATH_MAX] = {0};
|
||||
GetLogicalDriveStrings(PATH_MAX, drives);
|
||||
wchar_t* p = drives;
|
||||
while(*p || *(p + 1)) {
|
||||
if(!*p) *p = ';';
|
||||
p++;
|
||||
}
|
||||
return string{(const char*)utf8_t(drives)}.replace("\\", "/").split(";");
|
||||
}
|
||||
|
||||
vector<string> list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
if(!path.endsWith("\\")) path.append("\\");
|
||||
path.append("*");
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA data;
|
||||
handle = FindFirstFile(utf16_t(path), &data);
|
||||
if(handle != INVALID_HANDLE_VALUE) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) {
|
||||
if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
inline auto directory::ufiles(const string& pathname, const string& pattern) -> vector<string> {
|
||||
if(!pathname) return {};
|
||||
|
||||
vector<string> list;
|
||||
string path = pathname;
|
||||
path.transform("/", "\\");
|
||||
if(!path.endsWith("\\")) path.append("\\");
|
||||
path.append("*");
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATA data;
|
||||
handle = FindFirstFile(utf16_t(path), &data);
|
||||
if(handle != INVALID_HANDLE_VALUE) {
|
||||
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
while(FindNextFile(handle, &data) != false) {
|
||||
if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
string name = (const char*)utf8_t(data.cFileName);
|
||||
if(name.match(pattern)) list.append(name);
|
||||
}
|
||||
}
|
||||
FindClose(handle);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
#else
|
||||
inline auto directoryIsFolder(DIR* dp, struct dirent* ep) -> bool {
|
||||
if(ep->d_type == DT_DIR) return true;
|
||||
|
@ -353,3 +276,7 @@ inline auto directory::copy(const string& source, const string& target) -> bool
|
|||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if defined(NALL_HEADER_ONLY)
|
||||
#include <nall/directory.cpp>
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#include <nall/dl.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|
||||
NALL_HEADER_INLINE auto library::open(const string& name, const string& path) -> bool {
|
||||
if(handle) close();
|
||||
if(path) {
|
||||
string filepath = {path, name, ".dll"};
|
||||
handle = (uintptr)LoadLibraryW(utf16_t(filepath));
|
||||
}
|
||||
if(!handle) {
|
||||
string filepath = {name, ".dll"};
|
||||
handle = (uintptr)LoadLibraryW(utf16_t(filepath));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
NALL_HEADER_INLINE auto library::openAbsolute(const string& name) -> bool {
|
||||
if(handle) close();
|
||||
handle = (uintptr)LoadLibraryW(utf16_t(name));
|
||||
return handle;
|
||||
}
|
||||
|
||||
NALL_HEADER_INLINE auto library::sym(const string& name) -> void* {
|
||||
if(!handle) return nullptr;
|
||||
return (void*)GetProcAddress((HMODULE)handle, name);
|
||||
}
|
||||
|
||||
NALL_HEADER_INLINE auto library::close() -> void {
|
||||
if(!handle) return;
|
||||
FreeLibrary((HMODULE)handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -87,35 +87,7 @@ inline auto library::close() -> void {
|
|||
handle = 0;
|
||||
}
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
inline auto library::open(const string& name, const string& path) -> bool {
|
||||
if(handle) close();
|
||||
if(path) {
|
||||
string filepath = {path, name, ".dll"};
|
||||
handle = (uintptr)LoadLibraryW(utf16_t(filepath));
|
||||
}
|
||||
if(!handle) {
|
||||
string filepath = {name, ".dll"};
|
||||
handle = (uintptr)LoadLibraryW(utf16_t(filepath));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline auto library::openAbsolute(const string& name) -> bool {
|
||||
if(handle) close();
|
||||
handle = (uintptr)LoadLibraryW(utf16_t(name));
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline auto library::sym(const string& name) -> void* {
|
||||
if(!handle) return nullptr;
|
||||
return (void*)GetProcAddress((HMODULE)handle, name);
|
||||
}
|
||||
|
||||
inline auto library::close() -> void {
|
||||
if(!handle) return;
|
||||
FreeLibrary((HMODULE)handle);
|
||||
handle = 0;
|
||||
}
|
||||
//defined in dl.cpp
|
||||
#else
|
||||
inline auto library::open(const string&, const string&) -> bool { return false; }
|
||||
inline auto library::openAbsolute(const string&) -> bool { return false; }
|
||||
|
@ -124,3 +96,7 @@ inline auto library::close() -> void {}
|
|||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if defined(NALL_HEADER_ONLY)
|
||||
#include <nall/dl.cpp>
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
#include <nall/file-map.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
#if defined(API_WINDOWS)
|
||||
|
||||
NALL_HEADER_INLINE auto file_map::open(const string& filename, u32 mode_) -> bool {
|
||||
close();
|
||||
if(file::exists(filename) && file::size(filename) == 0) return _open = true;
|
||||
|
||||
s32 desiredAccess, creationDisposition, protection, mapAccess;
|
||||
|
||||
switch(mode_) {
|
||||
default: return false;
|
||||
case mode::read:
|
||||
desiredAccess = GENERIC_READ;
|
||||
creationDisposition = OPEN_EXISTING;
|
||||
protection = PAGE_READONLY;
|
||||
mapAccess = FILE_MAP_READ;
|
||||
break;
|
||||
case mode::write:
|
||||
//write access requires read access
|
||||
desiredAccess = GENERIC_WRITE;
|
||||
creationDisposition = CREATE_ALWAYS;
|
||||
protection = PAGE_READWRITE;
|
||||
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case mode::modify:
|
||||
desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
creationDisposition = OPEN_EXISTING;
|
||||
protection = PAGE_READWRITE;
|
||||
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case mode::append:
|
||||
desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
creationDisposition = CREATE_NEW;
|
||||
protection = PAGE_READWRITE;
|
||||
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
_file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr,
|
||||
creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if(_file == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
_size = GetFileSize(_file, nullptr);
|
||||
|
||||
_map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr);
|
||||
if(_map == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_file);
|
||||
_file = INVALID_HANDLE_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_data = (u8*)MapViewOfFile(_map, mapAccess, 0, 0, _size);
|
||||
return _open = true;
|
||||
}
|
||||
|
||||
NALL_HEADER_INLINE auto file_map::close() -> void {
|
||||
if(_data) {
|
||||
UnmapViewOfFile(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
if(_map != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_map);
|
||||
_map = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if(_file != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_file);
|
||||
_file = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
_open = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -73,76 +73,9 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
auto open(const string& filename, u32 mode_) -> bool {
|
||||
close();
|
||||
if(file::exists(filename) && file::size(filename) == 0) return _open = true;
|
||||
auto open(const string& filename, u32 mode_) -> bool;
|
||||
|
||||
s32 desiredAccess, creationDisposition, protection, mapAccess;
|
||||
|
||||
switch(mode_) {
|
||||
default: return false;
|
||||
case mode::read:
|
||||
desiredAccess = GENERIC_READ;
|
||||
creationDisposition = OPEN_EXISTING;
|
||||
protection = PAGE_READONLY;
|
||||
mapAccess = FILE_MAP_READ;
|
||||
break;
|
||||
case mode::write:
|
||||
//write access requires read access
|
||||
desiredAccess = GENERIC_WRITE;
|
||||
creationDisposition = CREATE_ALWAYS;
|
||||
protection = PAGE_READWRITE;
|
||||
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case mode::modify:
|
||||
desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
creationDisposition = OPEN_EXISTING;
|
||||
protection = PAGE_READWRITE;
|
||||
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case mode::append:
|
||||
desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
creationDisposition = CREATE_NEW;
|
||||
protection = PAGE_READWRITE;
|
||||
mapAccess = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
_file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr,
|
||||
creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if(_file == INVALID_HANDLE_VALUE) return false;
|
||||
|
||||
_size = GetFileSize(_file, nullptr);
|
||||
|
||||
_map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr);
|
||||
if(_map == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_file);
|
||||
_file = INVALID_HANDLE_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_data = (u8*)MapViewOfFile(_map, mapAccess, 0, 0, _size);
|
||||
return _open = true;
|
||||
}
|
||||
|
||||
auto close() -> void {
|
||||
if(_data) {
|
||||
UnmapViewOfFile(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
if(_map != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_map);
|
||||
_map = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if(_file != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_file);
|
||||
_file = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
_open = false;
|
||||
}
|
||||
auto close() -> void;
|
||||
|
||||
#else
|
||||
|
||||
|
@ -229,3 +162,7 @@ public:
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
#if defined(NALL_HEADER_ONLY)
|
||||
#include <nall/file-map.cpp>
|
||||
#endif
|
||||
|
|
|
@ -68,7 +68,7 @@ struct file : inode {
|
|||
struct __stat64 data;
|
||||
_wstat64(utf16_t(filename), &data);
|
||||
#endif
|
||||
return S_ISREG(data.st_mode) ? data.st_size : 0u;
|
||||
return (data.st_mode & S_IFMT) == S_IFREG ? data.st_size : 0u;
|
||||
}
|
||||
|
||||
static auto read(const string& filename) -> vector<u8> {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue