Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iQue Player support #1731

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7e661b9
started adding stuff
Jhynjhiruu Dec 2, 2024
c2fdc2d
get bootrom going
Jhynjhiruu Dec 2, 2024
2923650
Initial Virage implementation
Thar0 Dec 2, 2024
a31af04
Implement cached accesses to internal SRAMs
Thar0 Dec 2, 2024
9335a39
MI_VERSION and COP0 PRId for iQue
Thar0 Dec 2, 2024
32a2e2a
Add access control and IDE
Jhynjhiruu Dec 2, 2024
8b9d6c4
Merge branch 'ique-player' of https://github.com/Jhynjhiruu/ares into…
Jhynjhiruu Dec 2, 2024
6e9a893
Guard iQue Player hardware with system check
Jhynjhiruu Dec 2, 2024
d237b32
Access, interrupt cleanup, logging
Jhynjhiruu Dec 3, 2024
032b00d
Correctly display iQue Player interrupts
Jhynjhiruu Dec 3, 2024
08f029e
Implement some of NAND and implement aes. Made it to SK
Thar0 Dec 4, 2024
647b2ad
Fix spares, redo PI buffer
Jhynjhiruu Dec 4, 2024
e84f8f0
Attempt to fill in serialisation and improve SKC handling
Jhynjhiruu Dec 5, 2024
af21457
Add USB, reach SA1
Thar0 Dec 5, 2024
a20604a
Fix SKC trap
Jhynjhiruu Dec 5, 2024
923bd6e
Initial ATB implementation, fix some other things, avoid crashing on …
Thar0 Dec 5, 2024
487bf3c
Initial BB SI implementation
Thar0 Dec 7, 2024
62dc68d
Fix some small issues with SI
Thar0 Dec 7, 2024
3f251c8
'Fix' ATB for more DMA alignments
Thar0 Dec 7, 2024
beca905
Rewrite atb
Thar0 Dec 8, 2024
a692de8
Remove redundant mask operation in atb
Thar0 Dec 8, 2024
6a84109
ATB bugfixes, format/tidy SI and Virage
Thar0 Dec 9, 2024
53cbd5e
Fix secure timer implementation, hopefully fix failing to enter secur…
Jhynjhiruu Dec 10, 2024
f46b3b3
Partially working RTC
Jhynjhiruu Dec 11, 2024
665e79e
Still not working
Jhynjhiruu Dec 12, 2024
c3261a7
Works!
Jhynjhiruu Dec 12, 2024
b74f8e6
Basic clock functionality works - needs hardware tests for edge cases
Jhynjhiruu Dec 16, 2024
85cdfff
NAND shenanigans
Jhynjhiruu Dec 16, 2024
50aa742
Page spares, NAND ECC, NAND multiplane writes
Thar0 Dec 17, 2024
1f7fbbb
Button (needs UI change I think)
Jhynjhiruu Dec 18, 2024
14d5bd9
Fix randomness, resetting to the menu now works
Jhynjhiruu Dec 20, 2024
3fdcce1
Fix NAND tracer, add LED state to the UI
Thar0 Dec 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ares/ares/platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ struct Platform {
virtual auto audio(Node::Audio::Stream) -> void {}
virtual auto input(Node::Input::Input) -> void {}
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
virtual auto showLED(b1 show) -> void {}
virtual auto setLED(b1 on) -> void {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LukeUsher is this OK? I don't know how to review changes to Platform.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need both show and set here? Shouldn’t the emulator core just call set and the ui handle it accordingly? A config option could be added in the gui to enable or disable LED display

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showLED does the job of the config option, it determines whether the LED will be shown in the UI at all. I think it makes more sense for the guest core to inform the UI that it needs the LED to be shown rather than leaving it to a user option? At least, it seems to fit better with a philosophy of not having too many knobs for users to have to turn.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to the power button, how to handle the activity LED is a pretty open question - it's not really vital information to show to the user, but it is definitely part of the hardware. The solution here wasn't really intended to be merged, I think, but rather just exists to get something to show the LED state.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some systems even have multiple software controlled status LEDs that we should show; I believe Sega CD has two,

I think I’ll create a method for cores to add widgets to the status bar in a similar way cores can add menu options for things like changing discs, then we can use that; the approach in the PR can be merged as a temporary implementation until this frame work exists

The long term plan is that the cores can append the required number of LED components and handle rendering them.

};

extern Platform* platform;
Expand Down
9 changes: 9 additions & 0 deletions ares/n64/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ ares.objects += ares-n64-rdram
ares.objects += ares-n64-cpu
ares.objects += ares-n64-rsp
ares.objects += ares-n64-rdp
ares.objects += ares-iqueplayer-virage
ares.objects += ares-iqueplayer-nand
ares.objects += ares-iqueplayer-aes
ares.objects += ares-iqueplayer-usb

$(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp
$(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp
Expand All @@ -38,6 +42,11 @@ $(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp
$(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp

$(object.path)/ares-iqueplayer-virage.o: $(ares.path)/n64/virage/virage.cpp
$(object.path)/ares-iqueplayer-nand.o: $(ares.path)/n64/nand/nand.cpp
$(object.path)/ares-iqueplayer-aes.o: $(ares.path)/n64/aes/aes.cpp
$(object.path)/ares-iqueplayer-usb.o: $(ares.path)/n64/usb/usb.cpp

ifeq ($(vulkan),true)
ifeq ($(platform),macos)
molten = $(ares.path)/../thirdparty/MoltenVK/libMoltenVK.dylib
Expand Down
5 changes: 5 additions & 0 deletions ares/n64/accuracy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ struct Accuracy {
// Emulate a region-locked console
static constexpr bool RegionLock = false;
};

struct AES {
static constexpr bool SISD = 0 | Reference | !ARCHITECTURE_SUPPORTS_SSE4_1;
static constexpr bool SIMD = !SISD;
};
};
177 changes: 177 additions & 0 deletions ares/n64/aes/aes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/* Vaguely based on Aes.c -- AES encryption / decryption
2017-01-24 : Igor Pavlov : Public domain */

#include <n64/n64.hpp>

namespace ares::Nintendo64 {

AES aes;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the actual algorithm should go to nall, as a way to document that this is a standard AES-128-CBC. It'd also help showing eg. exactly the state there is on iQue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this; nall already has a construct for cryptography, so assuming it is the standard algorithm it should go there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've bound this implementation very closely with the N64 core resident structures, especially Memory::Writable, mostly due to performance concerns. AES decryption happens on essentially any PI DMA so it seems important that the implementation is streamlined for keeping VPS high.

If you could give more specific direction on how to make the implementation more generic without going through some glue code I can adjust it accordingly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very good point, we'll keep it here then if it's specialised to the memory layout!

#include "serialization.cpp"

static const u8 InvS[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
};

static inline u32 U32(u8 a0, u8 a1, u8 a2, u8 a3) {
return (a0 << 24) | (a1 << 16) | (a2 << 8) | (a3 << 0);
}

template<u32 n>
static inline u8 GB(u32 x) {
return (x >> (24 - 8 * n)) & 0xFF;
}

AES::AES() {
#define xtime(x) ((((x) << 1) ^ (((x) & 0x80) ? 0x1B : 0)) & 0xFF)
for (auto i : range(256)) {
u32 a1 = InvS[i];
u32 a2 = xtime(a1);
u32 a4 = xtime(a2);
u32 a8 = xtime(a4);
u32 a9 = a8 ^ a1;
u32 aB = a8 ^ a2 ^ a1;
u32 aD = a8 ^ a4 ^ a1;
u32 aE = a8 ^ a4 ^ a2;
D[0][i] = U32(aE, a9, aD, aB);
D[1][i] = U32(aB, aE, a9, aD);
D[2][i] = U32(aD, aB, aE, a9);
D[3][i] = U32(a9, aD, aB, aE);
}
#undef xtime
}

auto AES::decodeCBC(Memory::Writable &mem, u32 pos, u32 numBlocks) -> void {
if constexpr(Accuracy::AES::SIMD) {
__m128i iv = mvIV;

for (; numBlocks >= 4; numBlocks -= 4, pos += 4 * AES_BLOCK_SIZE) {
__m128i *key = mvKey;
__m128i i0, i1, i2, i3;
__m128i m0, m1, m2, m3;
__m128i t;

i0 = read128(mem, pos + 0 * AES_BLOCK_SIZE);
i1 = read128(mem, pos + 1 * AES_BLOCK_SIZE);
i2 = read128(mem, pos + 2 * AES_BLOCK_SIZE);
i3 = read128(mem, pos + 3 * AES_BLOCK_SIZE);

t = *key++;
m0 = _mm_xor_si128(i0, t);
m1 = _mm_xor_si128(i1, t);
m2 = _mm_xor_si128(i2, t);
m3 = _mm_xor_si128(i3, t);

for (auto r : range(9)) {
t = *key++;
m0 = _mm_aesdec_si128(m0, t);
m1 = _mm_aesdec_si128(m1, t);
m2 = _mm_aesdec_si128(m2, t);
m3 = _mm_aesdec_si128(m3, t);
}
t = *key++;
m0 = _mm_aesdeclast_si128(m0, t);
m1 = _mm_aesdeclast_si128(m1, t);
m2 = _mm_aesdeclast_si128(m2, t);
m3 = _mm_aesdeclast_si128(m3, t);

t = _mm_xor_si128(m0, iv); iv = i0; write128(mem, pos + 0 * AES_BLOCK_SIZE, t);
t = _mm_xor_si128(m1, iv); iv = i1; write128(mem, pos + 1 * AES_BLOCK_SIZE, t);
t = _mm_xor_si128(m2, iv); iv = i2; write128(mem, pos + 2 * AES_BLOCK_SIZE, t);
t = _mm_xor_si128(m3, iv); iv = i3; write128(mem, pos + 3 * AES_BLOCK_SIZE, t);
}

while (numBlocks--) {
__m128i *key = mvKey;
__m128i i = read128(mem, pos);
__m128i m = _mm_xor_si128(*key++, i);

for (auto r : range(9))
m = _mm_aesdec_si128(m, *key++);
m = _mm_aesdeclast_si128(m, *key++);

m = _mm_xor_si128(m, iv); iv = i; write128(mem, pos, m);
pos += AES_BLOCK_SIZE;
}

mvIV = iv;
}

if constexpr(Accuracy::AES::SISD) {
for (; numBlocks != 0; numBlocks--, pos += AES_BLOCK_SIZE) {
const u32 *key = mKey;
u32 out[4];
u32 in[4];
u32 s[4];
u32 t[4];

in[0] = mem.read<Word>(pos + 0 * 4);
in[1] = mem.read<Word>(pos + 1 * 4);
in[2] = mem.read<Word>(pos + 2 * 4);
in[3] = mem.read<Word>(pos + 3 * 4);

s[0] = in[0] ^ key[0];
s[1] = in[1] ^ key[1];
s[2] = in[2] ^ key[2];
s[3] = in[3] ^ key[3];
key += 4;

#define HD(s, i, x) D[x][GB<x>(s[(i - x) & 3])]
#define HD4(s, i) (HD(s, i, 0) ^ HD(s, i, 1) ^ HD(s, i, 2) ^ HD(s, i, 3))
for (auto r : range(4)) {
t[0] = HD4(s, 0) ^ key[0];
t[1] = HD4(s, 1) ^ key[1];
t[2] = HD4(s, 2) ^ key[2];
t[3] = HD4(s, 3) ^ key[3];
key += 4;
s[0] = HD4(t, 0) ^ key[0];
s[1] = HD4(t, 1) ^ key[1];
s[2] = HD4(t, 2) ^ key[2];
s[3] = HD4(t, 3) ^ key[3];
key += 4;
}
t[0] = HD4(s, 0) ^ key[0];
t[1] = HD4(s, 1) ^ key[1];
t[2] = HD4(s, 2) ^ key[2];
t[3] = HD4(s, 3) ^ key[3];
key += 4;
#undef HD4
#undef HD

#define FD(s, i, x) InvS[GB<x>(s[(i - x) & 3])]
#define FD4(s, i) U32(FD(s, i, 0), FD(s, i, 1), FD(s, i, 2), FD(s, i, 3))
out[0] = mIV[0] ^ FD4(t, 0) ^ key[0];
out[1] = mIV[1] ^ FD4(t, 1) ^ key[1];
out[2] = mIV[2] ^ FD4(t, 2) ^ key[2];
out[3] = mIV[3] ^ FD4(t, 3) ^ key[3];
#undef FD4
#undef FD

mIV[0] = in[0];
mIV[1] = in[1];
mIV[2] = in[2];
mIV[3] = in[3];

mem.write<Word>(pos + 0 * 4, out[0]);
mem.write<Word>(pos + 1 * 4, out[1]);
mem.write<Word>(pos + 2 * 4, out[2]);
mem.write<Word>(pos + 3 * 4, out[3]);
}
}
}

}
61 changes: 61 additions & 0 deletions ares/n64/aes/aes.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//iQue Player AES-128-CBC Decryption

class AES {
public:
AES();

static constexpr u32 AES_BLOCK_SIZE = 0x10;

static inline auto swap128(__m128i &v) -> __m128i {
return _mm_shuffle_epi8(v, _mm_set_epi8(12,13,14,15, 8,9,10,11, 4,5,6,7, 0,1,2,3));
}

static inline auto read128(Memory::Writable &mem, u32 p) -> __m128i {
return swap128(*(__m128i *)&mem.data[p & mem.maskWord]);
}

static inline auto write128(Memory::Writable &mem, u32 p, __m128i &v) -> void {
*(__m128i *)&mem.data[p & mem.maskWord] = swap128(v);
}

inline auto setKey(Memory::Writable& key) -> void {
if constexpr(Accuracy::AES::SIMD) {
for (auto i : range(0xB0 / 0x10))
mvKey[i] = read128(key, 0x420 + i * 0x10);
}

if constexpr(Accuracy::AES::SISD) {
for (auto i : range(0xB0 / 4))
mKey[i] = key.read<Word>(0x420 + i * 4);
}
}

inline auto setIV(Memory::Writable& iv, u32 offset) -> void {
if constexpr(Accuracy::AES::SIMD) {
mvIV = read128(iv, offset);
}

if constexpr(Accuracy::AES::SISD) {
for (auto i : range(0x10 / 4))
mIV[i] = iv.read<Word>(offset + i * 4);
}
}

auto decodeCBC(Memory::Writable& mem, u32 pos, u32 numBlocks) -> void;

//serialization.cpp
auto serialize(serializer&) -> void;

private:
u32 D[4][256];
union {
u32 mIV[0x10 / 4];
__m128i mvIV;
};
union {
u32 mKey[0xB0 / 4];
__m128i mvKey[0xB0 / 0x10];
};
};

extern AES aes;
5 changes: 5 additions & 0 deletions ares/n64/aes/serialization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
auto AES::serialize(serializer& s) -> void {
s(D);
s(mIV);
s(mKey);
}
3 changes: 3 additions & 0 deletions ares/n64/controller/gamepad/gamepad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Gamepad::Gamepad(Node::Port parent) {
r = node->append<Node::Input::Button>("R");
z = node->append<Node::Input::Button>("Z");
start = node->append<Node::Input::Button>("Start");

if(system._BB() && (parent->name() == "Controller Port 1"))
bb_button = node->append<Node::Input::Button>("Power");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The N64 core misses emulation of the Reset hardware button. I believe that should go into a menu item rather than being a controller button (also because on N64, that's not on the controller). On iQue it's more blurry because the controller is the console, so maybe this implementation is fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “reset” option in the GUI will call system.power() with reset = true signify reset instead of hard power cycle; this might be a good place to handle this for both n64 and dd since it works with the ares (soft)reset already.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely the part of the PR that needs the most work - it isn't really clear how this should be handled here, since it's so different to other platforms.
I can only think of one other system where the reset button is required for gameplay (that one Megadrive game that required it to progress), but in the iQue Player's case it's also required to save.
I did consider connecting it to system.power(), but that then raises the question of how to handle powering the console off, since from a hardware perspective it's the same button; the console is powered off from software via the INT2 handler before the NMI has a chance to occur. That in itself is another topic that needs some discussion.
The approach I went for in this draft PR is really awful and slows down the emulation quite a lot (since it's polling the controller every 32 clock cycles) and still isn't accurate to hardware; it's definitely a blocker on merging.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ares doesn't currently support powering a console off really; it is something that should be addressed eventually but as it stands, the only feature we expose is the initial power on (power:reset = false) and reset (power: reset = true)

So I'm not sure how to best handle this here...

Implementing power:reset=trueshould be done anyway as it allows the 'reset' menu item to work correctly in ares, though!


Gamepad::~Gamepad() {
Expand Down
1 change: 1 addition & 0 deletions ares/n64/controller/gamepad/gamepad.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct Gamepad : Controller {
Node::Input::Button r;
Node::Input::Button z;
Node::Input::Button start;
Node::Input::Button bb_button;

Gamepad(Node::Port);
~Gamepad();
Expand Down
5 changes: 4 additions & 1 deletion ares/n64/controller/port.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setDisconnect([&] { device.reset(); });
port->setSupported({"Gamepad", "Mouse"});
if(system._BB() && (name == "Controller Port 1"))
port->setSupported({"Gamepad"});
else
port->setSupported({"Gamepad", "Mouse"});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe Mouse in not support on iQue at all? Why allowing it for other ports?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to the button issue discussed above - the approach used in this draft is a quick hack to get the button working at all so I could test it, and many aspects of it need redoing.

}

auto ControllerPort::unload() -> void {
Expand Down
27 changes: 22 additions & 5 deletions ares/n64/cpu/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,33 @@ auto CPU::main() -> void {
vi.refreshed = false;
queue.remove(Queue::GDB_Poll);
if(GDB::server.hasClient()) {
queue.insert(Queue::GDB_Poll, (93750000*2)/60/240);
queue.insert(Queue::GDB_Poll, system.frequency()/60/240);
}
}

auto CPU::gdbPoll() -> void {
if(GDB::server.hasClient()) {
GDB::server.updateLoop();
queue.insert(Queue::GDB_Poll, (93750000*2)/60/240);
queue.insert(Queue::GDB_Poll, system.frequency()/60/240);
}
}

auto CPU::synchronize() -> void {
auto clocks = Thread::clock;
Thread::clock = 0;

mi.clock -= clocks;
vi.clock -= clocks;
ai.clock -= clocks;
rsp.clock -= clocks;
rdp.clock -= clocks;
pif.clock -= clocks;
if(!system._BB()) pif.clock -= clocks;
mi.main();
vi.main();
ai.main();
rsp.main();
rdp.main();
pif.main();
if(!system._BB()) pif.main();

queue.step(clocks, [](u32 event) {
switch(event) {
Expand All @@ -74,6 +76,12 @@ auto CPU::synchronize() -> void {
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::VIRAGE0_Command: return virage0.commandFinished();
case Queue::VIRAGE1_Command: return virage1.commandFinished();
case Queue::VIRAGE2_Command: return virage2.commandFinished();
case Queue::NAND_Command: return pi.nandCommandFinished();
case Queue::AES_Command: return pi.aesCommandFinished();
case Queue::BB_RTC_Tick: return pi.bb_rtc.tickClock();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we prefix all of this enums with `BB_"?

case Queue::RTC_Tick: return cartridge.rtc.tick();
case Queue::DD_Clock_Tick: return dd.rtc.tickClock();
case Queue::DD_MECHA_Response: return dd.mechaResponse();
Expand All @@ -98,7 +106,8 @@ auto CPU::instruction() -> void {
return exception.interrupt();
}
}
if (scc.nmiPending) {
if (scc.nmiPending || scc.nmiStrobe) {
scc.nmiStrobe = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why we need two different strobe lines here? I guess one should be sufficient?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because the NMI behaviour described in the CPU manual doesn't match how ares currently implements it (and how the PIF emulation seems to expect it). Really, we need someone familiar with the CPU core to look into it and figure out exactly how it's supposed to work - this was our approach to get the hardware behaviour to work without disturbing the existing code.

debugger.nmi();
step(1 * 2);
return exception.nmi();
Expand Down Expand Up @@ -163,6 +172,14 @@ auto CPU::power(bool reset) -> void {
fenv.setRound(float_env::toNearest);
context.setMode();

if (system._BB()) {
//Note: iQue divmode is still 1:1.5 but the value reported is different
scc.configuration.systemClockRatio = 1;

scc.coprocessor.revision = 0x40;
scc.coprocessor.implementation = 0x0B;
}

if constexpr(Accuracy::CPU::Recompiler) {
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
recompiler.allocator.resize(64_MiB, bump_allocator::executable, buffer);
Expand Down
Loading
Loading