Merge remote-tracking branch 'upstream/main' into ed64-basic

This commit is contained in:
Robin Jones 2024-05-05 15:20:55 +01:00
commit 8fd419662a
17 changed files with 822 additions and 24 deletions

View File

@ -77,7 +77,7 @@ jobs:
# continue-on-error: true # continue-on-error: true
- name: Upload rolling release - name: Upload rolling release
uses: softprops/action-gh-release@v0.1.15 uses: softprops/action-gh-release@v2
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
with: with:
name: Rolling release name: Rolling release
@ -105,7 +105,7 @@ jobs:
doxyfile-path: './Doxyfile' doxyfile-path: './Doxyfile'
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v4
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -23,6 +23,7 @@ N64_CFLAGS += -iquote $(SOURCE_DIR) -iquote $(ASSETS_DIR) -I $(SOURCE_DIR)/libs
SRCS = \ SRCS = \
main.c \ main.c \
boot/boot.c \ boot/boot.c \
boot/cheats.c \
boot/cic.c \ boot/cic.c \
boot/reboot.S \ boot/reboot.S \
flashcart/64drive/64drive_ll.c \ flashcart/64drive/64drive_ll.c \

View File

@ -82,11 +82,8 @@ If required, you can manually adjust the file on the SD card using your computer
- Download the latest `sc64menu.n64` file from the releases page, then put it in the root directory of your SD card. - Download the latest `sc64menu.n64` file from the releases page, then put it in the root directory of your SD card.
##### 64DD disk support ##### 64DD disk support
For the ability to load and run 64DD disk images, you need to add the folder `/menu/64ddipl` on the SD card. For the ability to load and run 64DD disk images, you need to place required 64DD IPL dumps in the `/menu/64ddipl` folder on the SD card.
Download and add the relevant ipl files and rename them before adding them to the folder: For more details follow [this guide on the 64dd.org website](https://64dd.org/tutorial_sc64.html).
- `NDDE0.n64` the US Prototype IPL can be downloaded from [here](https://64dd.org/dumps/64DD_IPL_US_MJR.n64)
- `NDXJ0.n64` the JPN Development IPL can be downloaded from [here](https://64dd.org/dumps/64DD_IPL_DEV_H4G.n64)
- `NDDJ2.n64` the JPN Retail IPL can be downloaded from [here](https://64dd.org/dumps/N64DD_IPLROM_(J).zip)
Note: to load an expansion disk (e.g. F-Zero X) browse to the N64 ROM and load it (but not start it) and then browse to the DD expansion file and press the `R` button. Note: to load an expansion disk (e.g. F-Zero X) browse to the N64 ROM and load it (but not start it) and then browse to the DD expansion file and press the `R` button.
@ -125,7 +122,6 @@ The ROMs can be found in the `output` directory.
Make sure that your firmware is compatible (currently v2.18.0+) Make sure that your firmware is compatible (currently v2.18.0+)
See: [here](https://github.com/Polprzewodnikowy/SummerCart64/blob/v2.18.0/docs/00_quick_startup_guide.md#firmware-backupupdate) See: [here](https://github.com/Polprzewodnikowy/SummerCart64/blob/v2.18.0/docs/00_quick_startup_guide.md#firmware-backupupdate)
#### From the devcontainer #### From the devcontainer
It is not currently possible to directly communicate with USB devices. It is not currently possible to directly communicate with USB devices.
BUT, as a work around you can use a proxy TCP/IP connection BUT, as a work around you can use a proxy TCP/IP connection
@ -143,6 +139,11 @@ Toggle the N64 power switch to load the ROM.
#### From the SD Card #### From the SD Card
A "release" version of the SC64 menu is called `sc64menu.n64` and can be created for when you want to add it directly to the root folder on the SDCard. This is generated by running `make all` or running `make sc64`. A "release" version of the SC64 menu is called `sc64menu.n64` and can be created for when you want to add it directly to the root folder on the SDCard. This is generated by running `make all` or running `make sc64`.
### Ares Emulator
For ease of development and debugging, the menu ROM is able to run in the Ares emulator (without most flashcart features).
* Ensure you have the Ares emulator on you computer.
* Load the `N64FlashcartMenu.n64` ROM.
### ED64 V3 over USB from your host (Windows) OS ### ED64 V3 over USB from your host (Windows) OS
* Download the UNFLoader [here](https://github.com/buu342/N64-UNFLoader/releases/download/v2.11/UNFLoader-Windows-x86.zip) * Download the UNFLoader [here](https://github.com/buu342/N64-UNFLoader/releases/download/v2.11/UNFLoader-Windows-x86.zip)
@ -157,7 +158,7 @@ A "release" version of the SC64 menu is called `sc64menu.n64` and can be created
# Update Libdragon submodule # Update Libdragon submodule
This repo currently uses the `unstable` branch as a submodule at a specific commit. This repo currently uses the `preview` branch as a submodule at a specific commit.
To update to the latest version, use `git submodule update --remote ` from the terminal. To update to the latest version, use `git submodule update --remote ` from the terminal.
# Generate documentation # Generate documentation

View File

@ -2,7 +2,9 @@
#include "boot_io.h" #include "boot_io.h"
#include "boot.h" #include "boot.h"
#include "cheats.h"
#include "cic.h" #include "cic.h"
#include "reboot.h"
#define C0_STATUS_FR (1 << 26) #define C0_STATUS_FR (1 << 26)
@ -10,10 +12,6 @@
#define C0_STATUS_CU1 (1 << 29) #define C0_STATUS_CU1 (1 << 29)
extern uint32_t reboot_start __attribute__((section(".text")));
extern size_t reboot_size __attribute__((section(".text")));
static io32_t *boot_get_device_base (boot_params_t *params) { static io32_t *boot_get_device_base (boot_params_t *params) {
io32_t *device_base_address = ROM_CART; io32_t *device_base_address = ROM_CART;
if (params->device_type == BOOT_DEVICE_TYPE_64DD) { if (params->device_type == BOOT_DEVICE_TYPE_64DD) {
@ -22,7 +20,7 @@ static io32_t *boot_get_device_base (boot_params_t *params) {
return device_base_address; return device_base_address;
} }
static void boot_detect_cic_seed (boot_params_t *params) { static cic_type_t boot_detect_cic (boot_params_t *params) {
io32_t *base = boot_get_device_base(params); io32_t *base = boot_get_device_base(params);
uint8_t ipl3[IPL3_LENGTH] __attribute__((aligned(8))); uint8_t ipl3[IPL3_LENGTH] __attribute__((aligned(8)));
@ -31,11 +29,17 @@ static void boot_detect_cic_seed (boot_params_t *params) {
dma_read_raw_async(ipl3, (uint32_t) (&base[16]), sizeof(ipl3)); dma_read_raw_async(ipl3, (uint32_t) (&base[16]), sizeof(ipl3));
dma_wait(); dma_wait();
params->cic_seed = cic_get_seed(cic_detect(ipl3)); return cic_detect(ipl3);
} }
void boot (boot_params_t *params) { void boot (boot_params_t *params) {
cic_type_t cic_type = boot_detect_cic(params);
if (params->detect_cic_seed) {
params->cic_seed = cic_get_seed(cic_type);
}
if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) { if (params->tv_type == BOOT_TV_TYPE_PASSTHROUGH) {
switch (get_tv_type()) { switch (get_tv_type()) {
case TV_PAL: case TV_PAL:
@ -53,10 +57,6 @@ void boot (boot_params_t *params) {
} }
} }
if (params->detect_cic_seed) {
boot_detect_cic_seed(params);
}
C0_WRITE_STATUS(C0_STATUS_CU1 | C0_STATUS_CU0 | C0_STATUS_FR); C0_WRITE_STATUS(C0_STATUS_CU1 | C0_STATUS_CU0 | C0_STATUS_FR);
while (!(cpu_io_read(&SP->SR) & SP_SR_HALT)); while (!(cpu_io_read(&SP->SR) & SP_SR_HALT));
@ -123,12 +123,16 @@ void boot (boot_params_t *params) {
cpu_io_write(&ipl3_dst[i], io_read((uint32_t) (&ipl3_src[i]))); cpu_io_write(&ipl3_dst[i], io_read((uint32_t) (&ipl3_src[i])));
} }
bool cheats_installed = cheats_install(cic_type, params->cheat_list);
register uint32_t skip_rdram_reset asm ("a0");
register uint32_t boot_device asm ("s3"); register uint32_t boot_device asm ("s3");
register uint32_t tv_type asm ("s4"); register uint32_t tv_type asm ("s4");
register uint32_t reset_type asm ("s5"); register uint32_t reset_type asm ("s5");
register uint32_t cic_seed asm ("s6"); register uint32_t cic_seed asm ("s6");
register uint32_t version asm ("s7"); register uint32_t version asm ("s7");
skip_rdram_reset = cheats_installed;
boot_device = (params->device_type & 0x01); boot_device = (params->device_type & 0x01);
tv_type = (params->tv_type & 0x03); tv_type = (params->tv_type & 0x03);
reset_type = BOOT_RESET_TYPE_COLD; reset_type = BOOT_RESET_TYPE_COLD;
@ -141,6 +145,7 @@ void boot (boot_params_t *params) {
asm volatile ( asm volatile (
"la $t3, reboot \n" "la $t3, reboot \n"
"jr $t3 \n" :: "jr $t3 \n" ::
[skip_rdram_reset] "r" (skip_rdram_reset),
[boot_device] "r" (boot_device), [boot_device] "r" (boot_device),
[tv_type] "r" (tv_type), [tv_type] "r" (tv_type),
[reset_type] "r" (reset_type), [reset_type] "r" (reset_type),

View File

@ -38,6 +38,7 @@ typedef struct {
boot_tv_type_t tv_type; boot_tv_type_t tv_type;
uint8_t cic_seed; uint8_t cic_seed;
bool detect_cic_seed; bool detect_cic_seed;
uint32_t *cheat_list;
} boot_params_t; } boot_params_t;

335
src/boot/cheats.c Normal file
View File

@ -0,0 +1,335 @@
#include <libdragon.h>
#include "boot_io.h"
#include "cheats.h"
#include "vr4300_asm.h"
#define HIT_INVALIDATE_I ((4 << 2) | 0)
#define HIT_WRITE_BACK_D ((6 << 2) | 1)
#define D_CACHE_LINE_SIZE (16)
#define CAUSE_IRQ_PRE_NMI (1 << 12)
#define CAUSE_EXC_CODE_MASK (0x7C)
#define CAUSE_EXC_CODE_WATCH (0x5C)
#define WATCHLO_W (1 << 0)
#define RELOCATED_EXCEPTION_HANDLER_ADDRESS (0x80000120)
#define EXCEPTION_HANDLER_ADDRESS (0x80000180)
#define PATCHER_ADDRESS (0x80700000)
#define ENGINE_TEMPORARY_ADDRESS (PATCHER_ADDRESS + 0x10000)
#define DEFAULT_ENGINE_ADDRESS (0x807C5C00)
typedef struct {
uint8_t type;
uint32_t address;
uint16_t value;
} cheat_t;
typedef struct {
cheat_t main;
cheat_t sub;
} cheat_entry_t;
typedef enum {
SPECIAL_DISABLE_EXPANSION_PAK = 0xEE,
SPECIAL_WRITE_BYTE_ON_BOOT = 0xF0,
SPECIAL_WRITE_SHORT_ON_BOOT = 0xF1,
SPECIAL_SET_STORE_LOCATION = 0xFF,
} cheat_type_special_t;
#define IS_WIDTH_16(t) ((t) & (1 << 0))
#define IS_CONDITION_NOT_EQUAL(t) ((t) & (1 << 1))
#define IS_CONDITION_GS_BUTTON(t) ((t) & (1 << 3))
#define IS_TYPE_REPEATER(t) ((t) == 0x50)
#define IS_TYPE_WRITE(t) ((((t)&0xF0) == 0x80) || (((t)&0xF0) == 0xA0))
#define IS_TYPE_CONDITIONAL(t) (((t)&0xF0) == 0xD0)
#define IS_DOUBLE_ENTRY(t) (IS_TYPE_CONDITIONAL(t) || IS_TYPE_REPEATER(t))
static bool cheats_patch_ipl3 (cic_type_t cic_type, io32_t *target) {
uint32_t patch_offset = 0;
uint32_t j_instruction = I_J((uint32_t)(target));
io32_t *ipl3 = SP_MEM->DMEM;
switch (cic_type) {
case CIC_5101: patch_offset = 476; break;
case CIC_6101:
case CIC_7102: patch_offset = 476; break;
case CIC_x102: patch_offset = 475; break;
case CIC_x103: patch_offset = 472; break;
case CIC_x105: patch_offset = 499; break;
case CIC_x106: patch_offset = 488; break;
default: return true;
}
// NOTE: Check for "jr $t1" instruction
// Libdragon IPL3 could be brute-force signed with any retail
// CIC seed and checksum, and we support only retail libultra IPL3
if (cpu_io_read(&ipl3[patch_offset]) != I_JR(REG_T1)) {
return false;
}
switch (cic_type) {
case CIC_x105:
// NOTE: This disables game code checksum verification
cpu_io_write(&ipl3[486], I_NOP());
break;
case CIC_x106:
// NOTE: CIC x106 IPL3 is partially scrambled
j_instruction ^= 0x8188764A;
break;
default: break;
}
cpu_io_write(&ipl3[patch_offset], j_instruction);
return false;
}
static bool cheats_get_next (uint32_t **cheat_list, cheat_entry_t *cheat) {
cheat_t *c = &cheat->main;
cheat->sub.type = 0;
for (int i = 0; i < 2; i++) {
uint32_t raw[2] = {(*cheat_list)[0], (*cheat_list)[1]};
(*cheat_list) += 2;
if ((raw[0] == 0) && (raw[1] == 0)) {
return false;
}
c->type = ((raw[0] >> 24) & 0xFF);
c->address = (raw[0] & 0xA07FFFFF);
c->value = (raw[1] & 0xFFFF);
if (!IS_DOUBLE_ENTRY(c->type)) {
break;
}
c = &cheat->sub;
}
return true;
}
static io32_t *cheats_get_engine_address (uint32_t *cheat_list) {
cheat_entry_t cheat;
while (cheats_get_next(&cheat_list, &cheat)) {
if (cheat.main.type == SPECIAL_SET_STORE_LOCATION) {
return (io32_t *)(cheat.main.address & 0x807FFFFF);
}
}
return (io32_t *)(DEFAULT_ENGINE_ADDRESS);
}
static void cheats_update_cache (volatile void *start, volatile void *end) {
data_cache_hit_writeback(start, (end - start));
inst_cache_hit_invalidate(start, (end - start));
}
bool cheats_install (cic_type_t cic_type, uint32_t *cheat_list) {
if (!cheat_list) {
return false;
}
io32_t *engine_start = (io32_t *)(ENGINE_TEMPORARY_ADDRESS);
io32_t *engine_p = engine_start;
io32_t *patcher_start = (io32_t *)(PATCHER_ADDRESS);
io32_t *patcher_p = patcher_start;
if (cheats_patch_ipl3(cic_type, patcher_start)) {
return false;
}
io32_t *final_engine_address = cheats_get_engine_address(cheat_list);
// Original watch exception handler code written by Jay Oster 'Parasyte'
// https://github.com/parasyte/alt64/blob/master/utils.c#L1024-L1054
uint32_t ori_placeholder_instruction = I_ORI(REG_ZERO, REG_K0, A_OFFSET(RELOCATED_EXCEPTION_HANDLER_ADDRESS));
uint32_t ori_placeholder_address = (uint32_t)(final_engine_address + 20);
// Load cause register
*engine_p++ = I_MFC0(REG_K0, C0_REG_CAUSE);
// Disable watch exception when reset button is pressed
*engine_p++ = I_ANDI(REG_K1, REG_K0, CAUSE_IRQ_PRE_NMI);
*engine_p++ = I_BNEL(REG_K1, REG_ZERO, 1);
*engine_p++ = I_MTC0(REG_ZERO, C0_REG_WATCH_LO);
// Check if watch exception ocurred, if yes then proceed to relocate the game exception handler
*engine_p++ = I_ANDI(REG_K0, REG_K0, CAUSE_EXC_CODE_MASK);
*engine_p++ = I_ORI(REG_K1, REG_ZERO, CAUSE_EXC_CODE_WATCH);
*engine_p++ = I_BNE(REG_K0, REG_K1, 15); // Skips to after the 'eret' instruction
// Extract base register number from the store instruction
*engine_p++ = I_MFC0(REG_K1, C0_REG_EPC);
*engine_p++ = I_LW(REG_K1, 0, REG_K1);
*engine_p++ = I_LUI(REG_K0, 0x03E0);
*engine_p++ = I_AND(REG_K1, REG_K0, REG_K1);
*engine_p++ = I_SRL(REG_K1, REG_K1, 5);
// Update create final instruction and update its target register number
*engine_p++ = I_LUI(REG_K0, ori_placeholder_instruction >> 16);
*engine_p++ = I_ORI(REG_K0, REG_K0, ori_placeholder_instruction);
*engine_p++ = I_OR(REG_K0, REG_K0, REG_K1);
// Write created instruction into placeholder
*engine_p++ = I_LUI(REG_K1, A_BASE(ori_placeholder_address));
*engine_p++ = I_SW(REG_K0, A_OFFSET(ori_placeholder_address), REG_K1);
// Force write and instruction cache invalidation
*engine_p++ = I_CACHE(HIT_WRITE_BACK_D, A_OFFSET(ori_placeholder_address), REG_K1);
*engine_p++ = I_CACHE(HIT_INVALIDATE_I, A_OFFSET(ori_placeholder_address), REG_K1);
// Load address base and execute created instruction
*engine_p++ = I_LUI(REG_K0, A_BASE(RELOCATED_EXCEPTION_HANDLER_ADDRESS));
*engine_p++ = I_NOP();
// Return from the exception
*engine_p++ = I_ERET();
cheat_entry_t cheat;
while (cheats_get_next(&cheat_list, &cheat)) {
cheat_t *c = &cheat.main;
if (IS_TYPE_REPEATER(c->type)) {
if ((!IS_TYPE_WRITE(cheat.sub.type)) || IS_CONDITION_GS_BUTTON(cheat.sub.type)) {
continue;
}
int count = ((c->address >> 8) & 0xFF);
int step = (c->address & 0xFF);
int16_t increment = (int16_t)(c->value);
c = &cheat.sub;
for (int i = 0; i < count; i++) {
*engine_p++ = I_LUI(REG_K0, A_BASE(c->address));
*engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value);
*engine_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0)
: I_SB(REG_K1, A_OFFSET(c->address), REG_K0);
c->address += step;
c->value += increment;
}
continue;
}
if (IS_TYPE_CONDITIONAL(c->type)) {
if ((!IS_TYPE_WRITE(cheat.sub.type)) || IS_CONDITION_GS_BUTTON(cheat.sub.type)) {
continue;
}
*engine_p++ = I_LUI(REG_K0, A_BASE(c->address));
*engine_p++ = IS_WIDTH_16(c->type) ? I_LHU(REG_K0, A_OFFSET(c->address), REG_K0)
: I_LBU(REG_K0, A_OFFSET(c->address), REG_K0);
*engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value & (IS_WIDTH_16(c->type) ? 0xFFFF : 0xFF));
*engine_p++ = IS_CONDITION_NOT_EQUAL(c->type) ? I_BEQ(REG_K0, REG_K1, 3) : I_BNE(REG_K0, REG_K1, 3);
c = &cheat.sub;
}
if (IS_TYPE_WRITE(c->type)) {
if (IS_CONDITION_GS_BUTTON(c->type)) {
continue;
}
*engine_p++ = I_LUI(REG_K0, A_BASE(c->address));
*engine_p++ = I_ORI(REG_K1, REG_ZERO, c->value);
*engine_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0)
: I_SB(REG_K1, A_OFFSET(c->address), REG_K0);
continue;
}
switch (c->type) {
case SPECIAL_WRITE_BYTE_ON_BOOT:
case SPECIAL_WRITE_SHORT_ON_BOOT: {
*patcher_p++ = I_LUI(REG_K0, A_BASE(c->address));
*patcher_p++ = I_ORI(REG_K1, REG_ZERO, c->value);
*patcher_p++ = IS_WIDTH_16(c->type) ? I_SH(REG_K1, A_OFFSET(c->address), REG_K0)
: I_SB(REG_K1, A_OFFSET(c->address), REG_K0);
break;
}
case SPECIAL_DISABLE_EXPANSION_PAK: {
*patcher_p++ = I_LUI(REG_K0, 0xA000);
*patcher_p++ = I_LUI(REG_K1, 0x0040);
*patcher_p++ = I_SW(REG_K1, 0x318, REG_K0);
*patcher_p++ = I_SW(REG_K1, 0x3F0, REG_K0);
break;
}
default: break;
}
}
*engine_p++ = I_J(RELOCATED_EXCEPTION_HANDLER_ADDRESS);
*engine_p++ = I_NOP();
uint32_t j_engine_from_handler = I_J((uint32_t)(final_engine_address));
// Copy engine to the final location
*patcher_p++ = I_LUI(REG_T3, A_BASE((uint32_t)(engine_start)));
*patcher_p++ = I_ADDIU(REG_T3, REG_T3, A_OFFSET((uint32_t)(engine_start)));
*patcher_p++ = I_LUI(REG_T4, A_BASE((uint32_t)(engine_p)));
*patcher_p++ = I_ADDIU(REG_T4, REG_T4, A_OFFSET((uint32_t)(engine_p)));
*patcher_p++ = I_LUI(REG_T5, A_BASE((uint32_t)(final_engine_address)));
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, A_OFFSET((uint32_t)(final_engine_address)));
*patcher_p++ = I_ORI(REG_T6, REG_ZERO, 0);
*patcher_p++ = I_LW(REG_K1, 0, REG_T3);
*patcher_p++ = I_SW(REG_K1, 0, REG_T5);
*patcher_p++ = I_ADDIU(REG_T3, REG_T3, 4);
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, 4);
*patcher_p++ = I_BNE(REG_T3, REG_T4, -5);
*patcher_p++ = I_ADDIU(REG_T6, REG_T6, 4);
// Force write and invalidate instruction cache
*patcher_p++ = I_LUI(REG_T5, A_BASE((uint32_t)(final_engine_address)));
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, A_OFFSET((uint32_t)(final_engine_address)));
*patcher_p++ = I_CACHE(HIT_WRITE_BACK_D, 0, REG_T5);
*patcher_p++ = I_CACHE(HIT_INVALIDATE_I, 0, REG_T5);
*patcher_p++ = I_ADDIU(REG_T6, REG_T6, -D_CACHE_LINE_SIZE);
*patcher_p++ = I_BGTZ(REG_T6, -4);
*patcher_p++ = I_ADDIU(REG_T5, REG_T5, D_CACHE_LINE_SIZE);
// Write jump instruction to the exception handler
*patcher_p++ = I_LUI(REG_K0, A_BASE(EXCEPTION_HANDLER_ADDRESS));
*patcher_p++ = I_ADDIU(REG_K0, REG_K0, A_OFFSET(EXCEPTION_HANDLER_ADDRESS));
*patcher_p++ = I_LUI(REG_K1, j_engine_from_handler >> 16);
*patcher_p++ = I_ORI(REG_K1, REG_K1, j_engine_from_handler);
*patcher_p++ = I_SW(REG_K1, 0, REG_K0);
*patcher_p++ = I_SW(REG_ZERO, 4, REG_K0);
*patcher_p++ = I_CACHE(HIT_WRITE_BACK_D, 0, REG_K0);
*patcher_p++ = I_CACHE(HIT_INVALIDATE_I, 0, REG_K0);
// Set watch exception on address 0x80000180
*patcher_p++ = I_ORI(REG_K1, REG_ZERO, EXCEPTION_HANDLER_ADDRESS | WATCHLO_W);
*patcher_p++ = I_MTC0(REG_K1, C0_REG_WATCH_LO);
*patcher_p++ = I_MTC0(REG_ZERO, C0_REG_WATCH_HI);
// Jump back to the game code
*patcher_p++ = I_JR(REG_T1);
*patcher_p++ = I_NOP();
cheats_update_cache(engine_start, engine_p);
cheats_update_cache(patcher_start, patcher_p);
return true;
}

10
src/boot/cheats.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef CHEATS_H__
#define CHEATS_H__
#include <stdint.h>
#include "cic.h"
bool cheats_install (cic_type_t cic_type, uint32_t *cheat_list);
#endif

View File

@ -1,3 +1,6 @@
#include "reboot.h"
#define IPL3_ENTRY 0xA4000040 #define IPL3_ENTRY 0xA4000040
#define REBOOT_ADDRESS 0xA4001000 #define REBOOT_ADDRESS 0xA4001000
#define STACK_ADDRESS 0xA4001FF0 #define STACK_ADDRESS 0xA4001FF0
@ -34,9 +37,10 @@ reboot_entry:
li $sp, STACK_ADDRESS li $sp, STACK_ADDRESS
reset_rdram: bnez $a0, reset_rdram_skip # Skip when cheats are enabled
bnez $s5, reset_rdram_skip bnez $s5, reset_rdram_skip # Skip when reset type is set to NMI
reset_rdram:
li $t0, RI_ADDRESS li $t0, RI_ADDRESS
sw $zero, RI_REFRESH($t0) sw $zero, RI_REFRESH($t0)

17
src/boot/reboot.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef REBOOT_H__
#define REBOOT_H__
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <stddef.h>
extern uint32_t reboot_start __attribute__((section(".text")));
extern size_t reboot_size __attribute__((section(".text")));
#endif
#endif

397
src/boot/vr4300_asm.h Normal file
View File

@ -0,0 +1,397 @@
#ifndef VR4300_ASM_H__
#define VR4300_ASM_H__
#include <stdint.h>
typedef union {
uint32_t raw;
struct {
uint32_t op : 6;
uint32_t rs : 5;
uint32_t rt : 5;
uint32_t imm : 16;
} i_type;
struct {
uint32_t op : 6;
uint32_t target : 26;
} j_type;
struct {
uint32_t op : 6;
uint32_t rs : 5;
uint32_t rt : 5;
uint32_t rd : 5;
uint32_t sa : 5;
uint32_t funct : 6;
} r_type;
struct {
uint32_t op : 6;
uint32_t co : 1;
uint32_t funct : 25;
} c_type;
} vr4300_instruction_t;
typedef enum {
OP_SPECIAL,
OP_REGIMM,
OP_J,
OP_JAL,
OP_BEQ,
OP_BNE,
OP_BLEZ,
OP_BGTZ,
OP_ADDI,
OP_ADDIU,
OP_SLTI,
OP_SLTIU,
OP_ANDI,
OP_ORI,
OP_XORI,
OP_LUI,
OP_COP0,
OP_COP1,
OP_COP2,
__OP_RESERVED_19,
OP_BEQL,
OP_BNEL,
OP_BLEZL,
OP_BGTZL,
OP_DADDI,
OP_DADDIU,
OP_LDL,
OP_LDR,
__OP_RESERVED_28,
__OP_RESERVED_29,
__OP_RESERVED_30,
__OP_RESERVED_31,
OP_LB,
OP_LH,
OP_LWL,
OP_LW,
OP_LBU,
OP_LHU,
OP_LWR,
OP_LWU,
OP_SB,
OP_SH,
OP_SWL,
OP_SW,
OP_SDL,
OP_SDR,
OP_SWR,
OP_CACHE,
OP_LL,
OP_LWC1,
OP_LWC2,
__OP_RESERVED_51,
OP_LLD,
OP_LDC1,
OP_LDC2,
OP_LD,
OP_SC,
OP_SWC1,
OP_SWC2,
__OP_RESERVED_59,
OP_SCD,
OP_SDC1,
OP_SDC2,
OP_SD,
} vr4300_op_t;
typedef enum {
FUNCT_SSL,
__FUNCT_RESERVED_1,
FUNCT_SRL,
FUNCT_SRA,
FUNCT_SLLV,
__FUNCT_RESERVED_5,
FUNCT_SRLV,
FUNCT_SRAV,
FUNCT_JR,
FUNCT_JALR,
__FUNCT_RESERVED_10,
__FUNCT_RESERVED_11,
FUNCT_SYSCALL,
FUNCT_BREAK,
__FUNCT_RESERVED_14,
FUNCT_SYNC,
FUNCT_MFHI,
FUNCT_MTHI,
FUNCT_MFLO,
FUNCT_MTLO,
FUNCT_DSLLV,
__FUNCT_RESERVED_21,
FUNCT_DSRLV,
FUNCT_DSRAV,
FUNCT_MULT,
FUNCT_MULTU,
FUNCT_DIV,
FUNCT_DIVU,
FUNCT_DMULT,
FUNCT_DMULTU,
FUNCT_DDIV,
FUNCT_DDIVU,
FUNCT_ADD,
FUNCT_ADDU,
FUNCT_SUB,
FUNCT_SUBU,
FUNCT_AND,
FUNCT_OR,
FUNCT_XOR,
FUNCT_NOR,
__FUNCT_RESERVED_40,
__FUNCT_RESERVED_41,
FUNCT_SLT,
FUNCT_SLTU,
FUNCT_DADD,
FUNCT_DADDU,
FUNCT_DSUB,
FUNCT_DSUBU,
FUNCT_TGE,
FUNCT_TGEU,
FUNCT_TLT,
FUNCT_TLTU,
FUNCT_TEQ,
__FUNCT_RESERVED_53,
FUNCT_TNE,
__FUNCT_RESERVED_55,
FUNCT_DSLL,
__FUNCT_RESERVED_57,
FUNCT_DSRL,
FUNCT_DSRA,
FUNCT_DSLL32,
__FUNCT_RESERVED_61,
FUNCT_DSRL32,
FUNCT_DSRA32,
} vr4300_funct_t;
typedef enum {
REGIMM_BLTZ,
REGIMM_BGEZ,
REGIMM_BLTZL,
REGIMM_BGEZL,
__REGIMM_RESERVED_4,
__REGIMM_RESERVED_5,
__REGIMM_RESERVED_6,
__REGIMM_RESERVED_7,
REGIMM_TGEI,
REGIMM_TGEIU,
REGIMM_TLTI,
REGIMM_TLTIU,
REGIMM_TEQI,
__REGIMM_RESERVED_13,
REGIMM_TNEI,
__REGIMM_RESERVED_15,
REGIMM_BLTZAL,
REGIMM_BGEZAL,
REGIMM_BLTZALL,
REGIMM_BGEZALL,
__REGIMM_RESERVED_20,
__REGIMM_RESERVED_21,
__REGIMM_RESERVED_22,
__REGIMM_RESERVED_23,
__REGIMM_RESERVED_24,
__REGIMM_RESERVED_25,
__REGIMM_RESERVED_26,
__REGIMM_RESERVED_27,
__REGIMM_RESERVED_28,
__REGIMM_RESERVED_29,
__REGIMM_RESERVED_30,
__REGIMM_RESERVED_31,
} vr4300_regimm_t;
typedef enum {
REG_ZERO,
REG_AT,
REG_V0,
REG_V1,
REG_A0,
REG_A1,
REG_A2,
REG_A3,
REG_T0,
REG_T1,
REG_T2,
REG_T3,
REG_T4,
REG_T5,
REG_T6,
REG_T7,
REG_S0,
REG_S1,
REG_S2,
REG_S3,
REG_S4,
REG_S5,
REG_S6,
REG_S7,
REG_T8,
REG_T9,
REG_K0,
REG_K1,
REG_GP,
REG_SP,
REG_FP,
REG_RA,
} vr4300_reg_t;
typedef enum {
C0_REG_INDEX,
C0_REG_RANDOM,
C0_REG_ENTRY_LO_0,
C0_REG_ENTRY_LO_1,
C0_REG_CONTEXT,
C0_REG_PAGE_MASK,
C0_REG_WIRED,
__C0_REG_RESERVED_7,
C0_REG_BAD_V_ADDR,
C0_REG_COUNT,
C0_REG_ENTRY_HI,
C0_REG_COMPARE,
C0_REG_STATUS,
C0_REG_CAUSE,
C0_REG_EPC,
C0_REG_PR_ID,
C0_REG_CONFIG,
C0_REG_LL_ADDR,
C0_REG_WATCH_LO,
C0_REG_WATCH_HI,
C0_REG_X_CONTEXT,
__C0_REG_RESERVED_21,
__C0_REG_RESERVED_22,
__C0_REG_RESERVED_23,
__C0_REG_RESERVED_24,
__C0_REG_RESERVED_25,
C0_REG_PARITY_ERROR,
C0_REG_CACHE_ERROR,
C0_REG_TAG_LO,
C0_REG_TAG_HI,
C0_REG_ERROR_EPC,
__C0_REG_RESERVED_31,
} vr4300_c0_reg_t;
typedef enum {
COPZ_RS_MF,
COPZ_RS_DMF,
COPZ_RS_CF,
__COPZ_RS_RESERVED_3,
COPZ_RS_MT,
COPZ_RS_DMT,
COPZ_RS_CT,
__COPZ_RS_RESERVED_7,
COPZ_RS_BC,
__COPZ_RS_RESERVED_9,
__COPZ_RS_RESERVED_10,
__COPZ_RS_RESERVED_11,
__COPZ_RS_RESERVED_12,
__COPZ_RS_RESERVED_13,
__COPZ_RS_RESERVED_14,
__COPZ_RS_RESERVED_15,
} vr4300_copz_rs_t;
typedef enum {
__C0_FUNCT_RESERVED_0,
C0_FUNCT_TLBR,
C0_FUNCT_TLBWI,
__C0_FUNCT_RESERVED_3,
__C0_FUNCT_RESERVED_4,
__C0_FUNCT_RESERVED_5,
C0_FUNCT_TLBWR,
__C0_FUNCT_RESERVED_7,
C0_FUNCT_TLBP,
__C0_FUNCT_RESERVED_9,
__C0_FUNCT_RESERVED_10,
__C0_FUNCT_RESERVED_11,
__C0_FUNCT_RESERVED_12,
__C0_FUNCT_RESERVED_13,
__C0_FUNCT_RESERVED_14,
__C0_FUNCT_RESERVED_15,
__C0_FUNCT_RESERVED_16,
__C0_FUNCT_RESERVED_17,
__C0_FUNCT_RESERVED_18,
__C0_FUNCT_RESERVED_19,
__C0_FUNCT_RESERVED_20,
__C0_FUNCT_RESERVED_21,
__C0_FUNCT_RESERVED_22,
__C0_FUNCT_RESERVED_23,
C0_FUNCT_ERET,
__C0_FUNCT_RESERVED_25,
__C0_FUNCT_RESERVED_26,
__C0_FUNCT_RESERVED_27,
__C0_FUNCT_RESERVED_28,
__C0_FUNCT_RESERVED_29,
__C0_FUNCT_RESERVED_30,
__C0_FUNCT_RESERVED_31,
__C0_FUNCT_RESERVED_32,
__C0_FUNCT_RESERVED_33,
__C0_FUNCT_RESERVED_34,
__C0_FUNCT_RESERVED_35,
__C0_FUNCT_RESERVED_36,
__C0_FUNCT_RESERVED_37,
__C0_FUNCT_RESERVED_38,
__C0_FUNCT_RESERVED_39,
__C0_FUNCT_RESERVED_40,
__C0_FUNCT_RESERVED_41,
__C0_FUNCT_RESERVED_42,
__C0_FUNCT_RESERVED_43,
__C0_FUNCT_RESERVED_44,
__C0_FUNCT_RESERVED_45,
__C0_FUNCT_RESERVED_46,
__C0_FUNCT_RESERVED_47,
__C0_FUNCT_RESERVED_48,
__C0_FUNCT_RESERVED_49,
__C0_FUNCT_RESERVED_50,
__C0_FUNCT_RESERVED_51,
__C0_FUNCT_RESERVED_52,
__C0_FUNCT_RESERVED_53,
__C0_FUNCT_RESERVED_54,
__C0_FUNCT_RESERVED_55,
__C0_FUNCT_RESERVED_56,
__C0_FUNCT_RESERVED_57,
__C0_FUNCT_RESERVED_58,
__C0_FUNCT_RESERVED_59,
__C0_FUNCT_RESERVED_60,
__C0_FUNCT_RESERVED_61,
__C0_FUNCT_RESERVED_62,
__C0_FUNCT_RESERVED_63,
} vr4300_c0_funct;
#define __ASM_I_INST(o, s, t, i) \
(((vr4300_instruction_t){.i_type = {.op = (o), .rs = (s), .rt = (t), .imm = (i)&0xFFFF}}).raw)
#define __ASM_J_INST(o, t) (((vr4300_instruction_t){.j_type = {.op = (o), .target = (t)&0x3FFFFFF}}).raw)
#define __ASM_R_INST(o, s, t, d, a, f) \
(((vr4300_instruction_t){.r_type = {.op = (o), .rs = (s), .rt = (t), .rd = (d), .sa = (a), .funct = (f)}}).raw)
#define __ASM_C_INST(o, c, f) (((vr4300_instruction_t){.c_type = {.op = (o), .co = (c), .funct = (f)}}).raw)
#define A_OFFSET(a) ((int16_t)((a)&0xFFFF))
#define A_BASE(a) ((uint16_t)((((a) >> 16) & 0xFFFF) + (A_OFFSET(a) < 0 ? 1 : 0)))
#define I_ADDIU(rt, rs, immediate) __ASM_I_INST(OP_ADDIU, rs, rt, immediate)
#define I_AND(rd, rs, rt) __ASM_R_INST(OP_SPECIAL, rs, rt, rd, 0, FUNCT_AND)
#define I_ANDI(rt, rs, immediate) __ASM_I_INST(OP_ANDI, rs, rt, immediate)
#define I_BEQ(rs, rt, offset) __ASM_I_INST(OP_BEQ, rs, rt, offset)
#define I_BGTZ(rs, offset) __ASM_I_INST(OP_BGTZ, rs, 0, offset)
#define I_BNE(rs, rt, offset) __ASM_I_INST(OP_BNE, rs, rt, offset)
#define I_BNEL(rs, rt, offset) __ASM_I_INST(OP_BNEL, rs, rt, offset)
#define I_CACHE(op, offset, base) __ASM_I_INST(OP_CACHE, base, op, offset)
#define I_ERET() __ASM_C_INST(OP_COP0, 1, C0_FUNCT_ERET)
#define I_J(target) __ASM_J_INST(OP_J, (target >> 2))
#define I_JR(rs) __ASM_R_INST(OP_SPECIAL, rs, REG_ZERO, REG_ZERO, 0, FUNCT_JR)
#define I_LBU(rt, offset, base) __ASM_I_INST(OP_LBU, base, rt, offset)
#define I_LHU(rt, offset, base) __ASM_I_INST(OP_LHU, base, rt, offset)
#define I_LUI(rt, immediate) __ASM_I_INST(OP_LUI, 0, rt, immediate)
#define I_LW(rt, offset, base) __ASM_I_INST(OP_LW, base, rt, offset)
#define I_MFC0(rt, rd) __ASM_R_INST(OP_COP0, COPZ_RS_MF, rt, rd, 0, 0)
#define I_MTC0(rt, rd) __ASM_R_INST(OP_COP0, COPZ_RS_MT, rt, rd, 0, 0)
#define I_NOP() __ASM_R_INST(OP_SPECIAL, REG_ZERO, REG_ZERO, REG_ZERO, 0, FUNCT_SSL)
#define I_OR(rd, rs, rt) __ASM_R_INST(OP_SPECIAL, rs, rt, rd, 0, FUNCT_OR)
#define I_ORI(rt, rs, immediate) __ASM_I_INST(OP_ORI, rs, rt, immediate)
#define I_SB(rt, offset, base) __ASM_I_INST(OP_SB, base, rt, offset)
#define I_SH(rt, offset, base) __ASM_I_INST(OP_SH, base, rt, offset)
#define I_SRL(rd, rt, sa) __ASM_R_INST(OP_SPECIAL, 0, rt, rd, sa, FUNCT_SRL)
#define I_SW(rt, offset, base) __ASM_I_INST(OP_SW, base, rt, offset)
#endif

View File

@ -2,7 +2,7 @@
### Official documentation ### Official documentation
http://64drive.retroactive.be/64drive_hardware_spec.pdf https://web.archive.org/web/20220611032320/http://64drive.retroactive.be/64drive_hardware_spec.pdf
### Save location offset in SDRAM ### Save location offset in SDRAM

View File

@ -147,12 +147,26 @@ static const match_t database[] = {
MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED) MATCH_HOMEBREW_HEADER("ED"), // Homebrew header (ED)
MATCH_CHECK_CODE(0x000000004CBC3B56, SAVE_TYPE_SRAM, FEAT_EXP_PAK_REQUIRED | FEAT_64DD_CONVERSION), // DMTJ 64DD cartridge conversion MATCH_CHECK_CODE(0x000000004CBC3B56, SAVE_TYPE_SRAM, FEAT_EXP_PAK_REQUIRED | FEAT_64DD_CONVERSION), // DMTJ 64DD cartridge conversion
MATCH_CHECK_CODE(0x0DD4ABABB5A2A91E, SAVE_TYPE_EEPROM_16K, FEAT_EXP_PAK_REQUIRED), // DK Retail kiosk demo MATCH_CHECK_CODE(0x0DD4ABABB5A2A91E, SAVE_TYPE_EEPROM_16K, FEAT_EXP_PAK_REQUIRED), // DK Retail kiosk demo
MATCH_CHECK_CODE(0xEB85EBC9596682AF, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Doubutsu Banchou MATCH_CHECK_CODE(0xEB85EBC9596682AF, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Doubutsu Banchou
MATCH_CHECK_CODE(0x9A746EBF2802EA99, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Toon panic
MATCH_CHECK_CODE(0x21548CA921548CA9, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Mini racers
MATCH_CHECK_CODE(0xBC9B2CC34ED04DA5, SAVE_TYPE_FLASHRAM, FEAT_NONE), // Starcraft 64 [Prototype 2000]
MATCH_CHECK_CODE(0x5D40ED2C10D6ABCF, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Viewpoint 2064
MATCH_CHECK_CODE(0x7280E03F497689BA, SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Viewpoint 2064 [ENG patch]
MATCH_CHECK_CODE(0xCDB8B4D08832352D, SAVE_TYPE_SRAM, FEAT_RPAK), // Jet Force Gemini [USA CRACK]
MATCH_CHECK_CODE(0xB66E0F7C2709C22F, SAVE_TYPE_SRAM, FEAT_RPAK), // Jet Force Gemini [PAL CRACK]
MATCH_CHECK_CODE(0xCE84793D27ECC1AD, SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Donkey kong 64 [USA CRACK]
MATCH_CHECK_CODE(0x1F95CAAA047FC22A, SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_EXP_PAK_REQUIRED), // Donkey kong 64 [PAL CRACK]
MATCH_CHECK_CODE(0xE3FF09DFCAE4B0ED, SAVE_TYPE_SRAM, FEAT_RPAK), // Banjo tooie [USA CRACK]
MATCH_ID_REGION_VERSION("NK4J", 0, SAVE_TYPE_SRAM, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] MATCH_ID_REGION_VERSION("NK4J", 0, SAVE_TYPE_SRAM, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)]
MATCH_ID_REGION_VERSION("NK4J", 1, SAVE_TYPE_SRAM, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] MATCH_ID_REGION_VERSION("NK4J", 1, SAVE_TYPE_SRAM, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)]
MATCH_ID("NK4", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)] MATCH_ID("NK4", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Kirby 64: The Crystal Shards [Hoshi no Kirby 64 (J)]
MATCH_ID_REGION_VERSION("NSMJ", 3, SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Super Mario 64 Shindou Edition MATCH_ID_REGION_VERSION("NSMJ", 3, SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Super Mario 64 Shindou Edition
MATCH_ID("NSM", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Super Mario 64 MATCH_ID("NSM", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Super Mario 64
@ -177,6 +191,7 @@ static const match_t database[] = {
MATCH_ID_REGION("NWTJ", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Wetrix MATCH_ID_REGION("NWTJ", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Wetrix
MATCH_ID("NWT", SAVE_TYPE_NONE, FEAT_CPAK), // Wetrix MATCH_ID("NWT", SAVE_TYPE_NONE, FEAT_CPAK), // Wetrix
// EEPROM 4K
MATCH_ID("CLB", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_64DD_ENHANCED), // Mario Party (NTSC) MATCH_ID("CLB", SAVE_TYPE_EEPROM_4K, FEAT_RPAK | FEAT_64DD_ENHANCED), // Mario Party (NTSC)
MATCH_ID("NAB", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Air Boarder 64 MATCH_ID("NAB", SAVE_TYPE_EEPROM_4K, FEAT_CPAK | FEAT_RPAK), // Air Boarder 64
MATCH_ID("NAD", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Worms Armageddon (U) MATCH_ID("NAD", SAVE_TYPE_EEPROM_4K, FEAT_NONE), // Worms Armageddon (U)
@ -271,6 +286,7 @@ static const match_t database[] = {
MATCH_ID("NXO", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Cruis'n Exotica MATCH_ID("NXO", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Cruis'n Exotica
MATCH_ID("NYK", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Yakouchuu II: Satsujin Kouro MATCH_ID("NYK", SAVE_TYPE_EEPROM_4K, FEAT_RPAK), // Yakouchuu II: Satsujin Kouro
// EEPROM 16K
MATCH_ID("N3D", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Doraemon 3: Nobita no Machi SOS! MATCH_ID("N3D", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Doraemon 3: Nobita no Machi SOS!
MATCH_ID("NB7", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Banjo-Tooie [Banjo to Kazooie no Daiboken 2 (J)] MATCH_ID("NB7", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Banjo-Tooie [Banjo to Kazooie no Daiboken 2 (J)]
MATCH_ID("NCW", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Cruis'n World MATCH_ID("NCW", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Cruis'n World
@ -295,12 +311,14 @@ static const match_t database[] = {
MATCH_ID("NUB", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_TPAK), // PD Ultraman Battle Collection 64 MATCH_ID("NUB", SAVE_TYPE_EEPROM_16K, FEAT_CPAK | FEAT_TPAK), // PD Ultraman Battle Collection 64
MATCH_ID("NYS", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Yoshi's Story MATCH_ID("NYS", SAVE_TYPE_EEPROM_16K, FEAT_RPAK), // Yoshi's Story
// SRAM 256K
MATCH_ID("CFZ", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // F-Zero X (J) MATCH_ID("CFZ", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // F-Zero X (J)
MATCH_ID("CPS", SAVE_TYPE_SRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium (J) MATCH_ID("CPS", SAVE_TYPE_SRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium (J)
MATCH_ID("CZL", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // Legend of Zelda: Ocarina of Time [Zelda no Densetsu - Toki no Ocarina (J)] MATCH_ID("CZL", SAVE_TYPE_SRAM, FEAT_RPAK | FEAT_64DD_ENHANCED), // Legend of Zelda: Ocarina of Time [Zelda no Densetsu - Toki no Ocarina (J)]
MATCH_ID("NA2", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Virtual Pro Wrestling 2 MATCH_ID("NA2", SAVE_TYPE_SRAM, FEAT_CPAK | FEAT_RPAK), // Virtual Pro Wrestling 2
MATCH_ID("NAL", SAVE_TYPE_SRAM, FEAT_RPAK), // Super Smash Bros. [Nintendo All-Star! Dairantou Smash Brothers (J)] MATCH_ID("NAL", SAVE_TYPE_SRAM, FEAT_RPAK), // Super Smash Bros. [Nintendo All-Star! Dairantou Smash Brothers (J)]
MATCH_ID("NB5", SAVE_TYPE_SRAM, FEAT_RPAK), // Biohazard 2 (J) MATCH_ID("NB5", SAVE_TYPE_SRAM, FEAT_RPAK), // Biohazard 2 (J)
MATCH_ID("NDD", SAVE_TYPE_SRAM, FEAT_EXP_PAK_REQUIRED | FEAT_64DD_CONVERSION), // 64DD Conversion Rom
MATCH_ID("NFZ", SAVE_TYPE_SRAM, FEAT_RPAK), // F-Zero X (U + E) MATCH_ID("NFZ", SAVE_TYPE_SRAM, FEAT_RPAK), // F-Zero X (U + E)
MATCH_ID("NG6", SAVE_TYPE_SRAM, FEAT_RPAK), // Ganmare Goemon: Dero Dero Douchuu Obake Tenkomori MATCH_ID("NG6", SAVE_TYPE_SRAM, FEAT_RPAK), // Ganmare Goemon: Dero Dero Douchuu Obake Tenkomori
MATCH_ID("NGP", SAVE_TYPE_SRAM, FEAT_CPAK), // Goemon: Mononoke Sugoroku MATCH_ID("NGP", SAVE_TYPE_SRAM, FEAT_CPAK), // Goemon: Mononoke Sugoroku
@ -333,11 +351,14 @@ static const match_t database[] = {
MATCH_ID("NYW", SAVE_TYPE_SRAM, FEAT_NONE), // Harvest Moon 64 MATCH_ID("NYW", SAVE_TYPE_SRAM, FEAT_NONE), // Harvest Moon 64
MATCH_ID("NZL", SAVE_TYPE_SRAM, FEAT_RPAK), // Legend of Zelda: Ocarina of Time (E) MATCH_ID("NZL", SAVE_TYPE_SRAM, FEAT_RPAK), // Legend of Zelda: Ocarina of Time (E)
// SRAM 768K
MATCH_ID("CDZ", SAVE_TYPE_SRAM_BANKED, FEAT_RPAK | FEAT_64DD_ENHANCED), // Dezaemon 3D MATCH_ID("CDZ", SAVE_TYPE_SRAM_BANKED, FEAT_RPAK | FEAT_64DD_ENHANCED), // Dezaemon 3D
// FLASHRAM
MATCH_ID("CP2", SAVE_TYPE_FLASHRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium 2 (J) MATCH_ID("CP2", SAVE_TYPE_FLASHRAM, FEAT_TPAK | FEAT_64DD_ENHANCED), // Pocket Monsters Stadium 2 (J)
MATCH_ID("NAF", SAVE_TYPE_FLASHRAM, FEAT_CPAK | FEAT_RTC), // Doubutsu no Mori MATCH_ID("NAF", SAVE_TYPE_FLASHRAM, FEAT_CPAK | FEAT_RTC), // Doubutsu no Mori
MATCH_ID("NCC", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Command & Conquer MATCH_ID("NCC", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // Command & Conquer
MATCH_ID("NCV", SAVE_TYPE_FLASHRAM, FEAT_NONE), // Cubivore (Translation)
MATCH_ID("NCK", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // NBA Courtside 2 featuring Kobe Bryant MATCH_ID("NCK", SAVE_TYPE_FLASHRAM, FEAT_RPAK), // NBA Courtside 2 featuring Kobe Bryant
MATCH_ID("NDA", SAVE_TYPE_FLASHRAM, FEAT_CPAK), // Derby Stallion 64 MATCH_ID("NDA", SAVE_TYPE_FLASHRAM, FEAT_CPAK), // Derby Stallion 64
MATCH_ID("NDP", SAVE_TYPE_FLASHRAM, FEAT_EXP_PAK_REQUIRED), // Dinosaur Planet (Unlicensed) MATCH_ID("NDP", SAVE_TYPE_FLASHRAM, FEAT_EXP_PAK_REQUIRED), // Dinosaur Planet (Unlicensed)
@ -356,6 +377,7 @@ static const match_t database[] = {
MATCH_ID("NP3", SAVE_TYPE_FLASHRAM_PKST2, FEAT_TPAK), // Pokemon Stadium 2 [Pocket Monsters Stadium - Kin Gin (J)] MATCH_ID("NP3", SAVE_TYPE_FLASHRAM_PKST2, FEAT_TPAK), // Pokemon Stadium 2 [Pocket Monsters Stadium - Kin Gin (J)]
// CONTROLLER PAK / NONE
MATCH_ID("N22", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ready 2 Rumble Boxing - Round 2 MATCH_ID("N22", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Ready 2 Rumble Boxing - Round 2
MATCH_ID("N2M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2002 MATCH_ID("N2M", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Madden Football 2002
MATCH_ID("N32", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Sarge's Heroes 2 MATCH_ID("N32", SAVE_TYPE_NONE, FEAT_CPAK | FEAT_RPAK), // Army Men - Sarge's Heroes 2

View File

@ -71,6 +71,7 @@ static void command_reboot (menu_t *menu) {
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
menu->boot_params->detect_cic_seed = true; menu->boot_params->detect_cic_seed = true;
menu->boot_params->cheat_list = NULL;
}; };
static void command_send_file (menu_t *menu) { static void command_send_file (menu_t *menu) {

View File

@ -8,7 +8,7 @@ static const char *n64_rom_extensions[] = { "z64", "n64", "v64", "rom", NULL };
static const char *text_extensions[] = { "txt", NULL }; static const char *text_extensions[] = { "txt", NULL };
static const char *config_extensions[] = { "ini", "cfg", "yml", "yaml", "toml", NULL }; static const char *config_extensions[] = { "ini", "cfg", "yml", "yaml", "toml", NULL };
static const char *save_extensions[] = { "sav", "eep", "eeprom", "sra", "srm", "ram", "fla", "flashram", NULL }; static const char *save_extensions[] = { "sav", "eep", "eeprom", "sra", "srm", "ram", "fla", "flashram", NULL };
static const char *patch_extensions[] = { "ips", "aps", "pps", "xdelta", NULL }; static const char *patch_extensions[] = { "aps", "bps", "ips", "pps", "ups", "xdelta", NULL };
static const char *archive_extensions[] = { "zip", "rar", "7z", "tar", "gz", NULL }; static const char *archive_extensions[] = { "zip", "rar", "7z", "tar", "gz", NULL };
static const char *image_extensions[] = { "png", "jpg", "gif", NULL }; static const char *image_extensions[] = { "png", "jpg", "gif", NULL };
static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL }; static const char *music_extensions[] = { "mp3", "wav", "ogg", "wma", "flac", NULL };

View File

@ -136,10 +136,12 @@ static void load (menu_t *menu) {
case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break;
default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break; default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break;
} }
menu->boot_params->cheat_list = NULL;
} else { } else {
menu->boot_params->device_type = BOOT_DEVICE_TYPE_64DD; menu->boot_params->device_type = BOOT_DEVICE_TYPE_64DD;
menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC; menu->boot_params->tv_type = BOOT_TV_TYPE_NTSC;
menu->boot_params->detect_cic_seed = true; menu->boot_params->detect_cic_seed = true;
menu->boot_params->cheat_list = NULL;
} }
} }

View File

@ -100,6 +100,7 @@ static void load (menu_t *menu) {
menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM;
menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH;
menu->boot_params->detect_cic_seed = true; menu->boot_params->detect_cic_seed = true;
menu->boot_params->cheat_list = NULL;
} }

View File

@ -310,6 +310,7 @@ static void load (menu_t *menu) {
case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break; case ROM_TV_TYPE_MPAL: menu->boot_params->tv_type = BOOT_TV_TYPE_MPAL; break;
default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break; default: menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; break;
} }
menu->boot_params->cheat_list = NULL;
} }
static void deinit (void) { static void deinit (void) {