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

Perfect Dark does not detect expansion pak #58

iamgreaser opened this issue Feb 17, 2017 · 6 comments

Perfect Dark does not detect expansion pak #58

iamgreaser opened this issue Feb 17, 2017 · 6 comments


Copy link

iamgreaser commented Feb 17, 2017

Using NTSC USA 8.7 final ROM. Perfect Dark does not detect the expansion pak, and thus the single-player campaign is missing.

This is a known bug.

Copy link

queueRAM commented May 3, 2017

I started digging into what different games use for expansion pak detection. This problem seems unique to PD. It might be a difference of reading global osMemSize vs. using osGetMemSize() which allegedly performs additional checks by writing to the extended memory. I'll keep digging through PD code to see which method it uses.
perfect dark small

At least a dozen other titles successfully detect the presence of an expansion pak under cen64 (build 30ce1c9)

Copy link

queueRAM commented May 4, 2017

I have identified the root cause of PD thinking the expansions pak isn't present, but am currently unsure how to fix it. The issue seems to be that IPL3 thinks the cold reset is an NMI and the game's code takes a different logic path based on this.

There is some code early on in the game around 0x700016C8 (TLB mapped, unsure where this is in physical RAM or ROM) that looks like:

if (reset_type == NMI) { // *0x80000308 == 1
  mem_size = saved_mem_size; // *0x8008dcb4 = *0x803f50b8;
} else { // cold reset
  mem_size = ipl3_mem_size; // *0x8008dcb4 = *0x80000318;

Unfortunately, under cen64, reset_type is identified by IPL3 as NMI during a cold reset, so the game tries to use its saved value of mem_size from 803f50b8, which is 0. It compares this mem_size 0x400001: if less than sets a "is_4mb" byte flag at 0x80090af0 = 1, else sets 0.

I've checked other games under cen64 and the word at 0x80000308 always appears to be 1 (reset_type = NMI) at the game entry point.

Copy link

tj90241 commented May 4, 2017

Interesting. I am not sure why this would be. I verified that Status.SR = 0 at CPU boot.

Copy link

queueRAM commented May 5, 2017

As you indicated, I see Status.SR (bit 20) = 0 at boot, but when the reset type is handed off to IPL3 through S5, it is 1 (reset_type = NMI). To verify this, I added the following code to vr4300_ex_stage() in vr4300/pipeline.c:

  if (rfex_latch->common.pc == 0xffffffffa4000040) { // IPL3
    debug("S3 = %08X\nS4 = %08X\nS5 = %08X\n",


S3 = 00000000
S4 = 00000001
S5 = 00000001

I don't yet know enough to understand how the data is propagated from the CP0 Status register into S5, but it looks like IPL2 is expecting the PIF to set magic bits set in PIF RAM offset 0x24 (bfc007e4/1fc007e4): S5 = ((*0xbfc007e4) >> 17) & 1. For Perfect Dark, the word loaded from 0xbfc007e4 is 0x2913F which is the CIC_SEED_NUS_6105. Is this seed value supposed to be stored here? Is the PIF supposed to overwrite it with something else at some point?

To increase my confidence that this is the issue with PD, I set a breakpoint on the debug() line above and merely change vr4300->regs[VR4300_REGISTER_S5] = 0 and the expansion pak detection in PD worked as expected:

Copy link

queueRAM commented May 8, 2017

I asked for clarification over on #n64dev and got a lot of good information. Long description below, but the problem is that the word in PIF RAM offset 0x24 contains status bits in addition to the CIC seed values. Specifically, bit 17 is the reset type (1 = NMI, 0 = cold reset).

bits     | reg | description
00080000 | S3  | osRomType (0=GamePack, 1=DD)
00040000 | S7  | osVersion
00020000 | S5  | osResetType (1 = NMI, 0 = cold reset)
0000FF00 | S6  | CIC IPL3 seed value
000000FF | --  | CIC IPL2 seed value
-------- | S4  | TV Type (0=PAL, 1=NTSC, 2=MPAL)

Therefore, I think the CIC seeds in si/cic.c should be updated as below and if NMI needs to be generated, 0x00020000 should be set. I've only tested this with Perfect Dark so far.

#define CIC_SEED_NUS_5101 0x0000AC00U
#define CIC_SEED_NUS_6101 0x00043F3FU
#define CIC_SEED_NUS_6102 0x00003F3FU
#define CIC_SEED_NUS_6103 0x0000783FU
#define CIC_SEED_NUS_6105 0x0000913FU
#define CIC_SEED_NUS_6106 0x0000853FU
#define CIC_SEED_NUS_8303 0x0000DD00U

For completeness, here is a summary of the N64 boot.

  • PIF talks with CIC and receives CIC seed value during PIF reset
  • PIF writes CIC seed value and other bits to word in PIF RAM at offset 0x24
  • IPL1 boots from PIF ROM
    • IPL1 sets VR4300 config and status registers, waits for SP to halt and SP DMA to finish
    • IPL1 resets PI, VI, AI registers
    • IPL1 copies IPL2 from PIF ROM to SP IMEM
  • IPL2 is jumped to from IPL1
    • IPL2 waits for PIF to signal PIF<-> CIC communications are finished
    • IPL2 reads CIC seed value and status bits from 0x24 and stores values in S3, S4, S5, S6, S7
    • IPL2 copies IPL3 from cart ROM to DMEM
  • IPL3 is jumped to from IPL2
    • IPL3 initializes RDRAM, clears ICache, DCache, traces of IPL1/IPL2
    • IPL3 copies cart code to RDRAM and verifies checksum

Thanks to _Happy_, marshallh, Zoinkity for explaining this info.

Copy link

tj90241 commented May 8, 2017

Looks good, and thanks for the info. Free free to make a PR... you've done all the hard work!

queueRAM added a commit to queueRAM/cen64 that referenced this issue May 9, 2017
Some CIC seed values had bit 0x20000 set which is the reset type
indicated from PIF (1 = NMI, 0 = Cold Reset). Some games
follow different initialization paths for NMIs which can cause
some unexpected boot behavior during a cold reset.

Fixes n64dev#58 Expansion Pak detection in Perfect Dark
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
None yet

No branches or pull requests

3 participants