update ares, fix some lingering issues with recompiler

This commit is contained in:
CasualPokePlayer 2023-03-29 04:00:10 -07:00
parent 00152bbaa9
commit 02caa5fcbb
191 changed files with 12558 additions and 5465 deletions

Binary file not shown.

View File

@ -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)

View File

@ -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

View File

@ -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>;

View File

@ -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);

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;

View File

@ -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(" ");

View File

@ -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());
}
}

View File

@ -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;
};
};

View File

@ -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;

View File

@ -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_;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -2,4 +2,5 @@ auto Cartridge::serialize(serializer& s) -> void {
s(ram);
s(eeprom);
s(flash);
s(rtc);
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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]");
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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 {}
};

View File

@ -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();

View File

@ -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

View File

@ -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; };
};

View File

@ -12,6 +12,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->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;

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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& {

View File

@ -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];

View File

@ -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)...};
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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,

View File

@ -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);
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}
};

View File

@ -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;

View File

@ -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_;

View File

@ -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;

View File

@ -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>

View File

@ -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);
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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_;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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_;

View File

@ -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;

View File

@ -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)...};
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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)

View File

@ -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_;

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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_;

View File

@ -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;

View File

@ -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 {

View File

@ -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); }
};
}

View File

@ -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)

View File

@ -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>

View File

@ -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

View File

@ -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; }

View File

@ -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) {
}
};

View File

@ -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); }

View File

@ -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 {};
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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