Skip to content

ED64 V Series Support - #342

Closed
lewilou22 wants to merge 3 commits into
n64brew:mainfrom
lewilou22:main
Closed

lewilou22 wants to merge 3 commits into
n64brew:mainfrom
lewilou22:main

Conversation

@lewilou22

@lewilou22 lewilou22 commented May 30, 2026 •

Copy link
Copy Markdown

Description

Saves for ED64 devices on V2 or clones

Motivation and Context

How Has This Been Tested?

Screenshots

Types of changes

  • [ X] Improvement (non-breaking change that adds a new feature)
  • [ X] Bug fix (fixes an issue)
  • Breaking change (breaking change)
  • Documentation Improvement
  • [X ] Config and build (change in the configuration and build system, has no impact on code or features)

Checklist:

  • [x ] My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

You agree with the license terms and that other license types may be granted with permission of the original N64FlashcartMenu project license holders.

Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>

Summary by CodeRabbit

  • New Features

    • EverDrive-64 V-series flashcart driver now includes save writeback functionality and persistent state management for enhanced ROM and save data handling.
  • Refactor

    • Updated driver implementation with improved low-level hardware interface and support for multiple save media types.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 30, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds a complete EverDrive-64 V-series flashcart driver to replace the prior implementation. It introduces low-level hardware primitives for save access, INI-based persistent state tracking, a main driver orchestrating ROM and save operations, and updates the build system and integration points.

Changes

EverDrive-64 Driver Implementation

Layer / File(s) Summary
Low-level ED64 save interface
src/flashcart/ed64/ed64_ll.h, src/flashcart/ed64/ed64_ll.c
Defines register constants and ed64_save_type_t enum for SRAM/EEPROM/FRAM media types, implements PI/DMA helpers and cache maintenance, and provides public accessors (ed64_ll_get_sram/eeprom/fram, ed64_ll_set_sram/eeprom/fram) that control hardware save operations via register bitfields and memory transfers.
State persistence via INI
src/flashcart/ed64/ed64_state.h, src/flashcart/ed64/ed64_state.c
Introduces ed64_pseudo_writeback_t struct tracking save-writeback expectations, FRAM type, and last save path; implements lifecycle functions (init, load, save, free) that read/write state from sd:/menu/ed64_state.ini and manage string memory for the save path.
ED64 driver orchestration
src/flashcart/ed64/ed64.h, src/flashcart/ed64/ed64.c
Implements main flashcart driver coordinating ROM loading (64MiB SDRAM window via chunked FatFS reads), save loading from SD into cart memory, save writeback on init if a prior save operation was detected, and state persistence; maps menu save types to hardware types and wires all operations into a flashcart_t vtable.
Driver integration and build
src/flashcart/flashcart.c, Makefile
Updates flashcart_init to instantiate ed64_get_flashcart() instead of the prior ed64_vseries_get_flashcart(), and modifies the Makefile to compile the three new ED64 source files while removing the old ed64_vseries.c from compilation.

Sequence Diagram(s)

sequenceDiagram
  participant Menu as N64Menu
  participant Driver as ed64_driver
  participant State as ed64_state
  participant LL as ed64_ll
  participant SD as SD Card
  Menu->>Driver: ed64_init()
  Driver->>State: ed64_state_load()
  State->>SD: read ed64_state.ini
  State-->>Driver: load state with writeback flag
  alt is_expecting_save_writeback
    Driver->>LL: ed64_ll_get_sram/eeprom/fram()
    LL-->>Driver: cart save buffer
    Driver->>SD: write buffer to last_save_path
    Driver->>State: clear flag and persist
  end
  Menu->>Driver: load_save(file)
  Driver->>SD: read save file
  Driver->>LL: ed64_ll_set_sram/eeprom/fram()
  Driver->>State: store save_path and mark next boot
  Driver->>State: ed64_state_save()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop along, dear N64 friends—
A drive arrives with endless bends!
State tracked soft in INI's grace,
Saves return to their rightful place.
Low-level dance with DMA's might,
ED64 shines ever bright! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ED64 V Series Support' is directly related to the main change: implementing EverDrive-64 V-series flashcart driver support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/flashcart/ed64/ed64_ll.c`:
- Around line 150-162: ed64_ll_get_sram starts a PI DMA into buffer via
pi_dma_from_sram but does not invalidate the CPU data cache for buffer, so stale
cached lines can be read later; add a destination cache invalidation (matching
the convention used by pi_dma_read_data and the FRAM path) immediately before
calling pi_dma_from_sram in ed64_ll_get_sram (e.g., call the platform
cache-invalidate helper such as osInvalDCache or the project's equivalent on
buffer/size), keeping the rest of the sequence (pi_initialize_sram, dma_wait,
pi_initialize, wait_ms) intact and ensuring ed64_ll_get_sram now invalidates
buffer before the DMA read.

In `@src/flashcart/ed64/ed64.c`:
- Around line 86-117: The early-return error paths in this block leave
current_state.is_expecting_save_writeback set, causing persistent boot failures;
before each return FLASHCART_ERR_LOAD in the save writeback flow (after
save_size validation, after ed64_apply_save_type failure, and on any file
write/close failure) clear current_state.is_expecting_save_writeback = false and
persist that change (call the existing state persistence routine, e.g.
ed64_persist_state() or the equivalent function used elsewhere) so the cleared
flag is saved to disk; do this immediately prior to each return to ensure the
flag is not left set across reboots.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 796e74ee-5779-42b8-943d-efd8033d8815

📥 Commits

Reviewing files that changed from the base of the PR and between 6407ab1 and c727563.

📒 Files selected for processing (8)
  • Makefile
  • src/flashcart/ed64/ed64.c
  • src/flashcart/ed64/ed64.h
  • src/flashcart/ed64/ed64_ll.c
  • src/flashcart/ed64/ed64_ll.h
  • src/flashcart/ed64/ed64_state.c
  • src/flashcart/ed64/ed64_state.h
  • src/flashcart/flashcart.c

Comment on lines +150 to +162
void ed64_ll_get_sram (uint8_t *buffer, int size) {
int offset = (size == (int) KiB(32)) ? 0 : SRAM_128K_OFFSET;

pi_initialize_sram();
dma_wait();
pi_initialize();
wait_ms(250);

pi_dma_from_sram(buffer, offset, size);
dma_wait();

ed64_ll_set_sdcard_timing();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Compare cache handling between read and write SRAM paths and any reference impl
rg -nP 'data_cache_hit_writeback_invalidate|pi_dma_from_sram|pi_dma_to_sram|PI_WR_LEN|PI_RD_LEN' -C2
# Look for any prior ed64 implementation for reference patterns
fd -i 'ed64' -e c

Repository: Polprzewodnikowy/N64FlashcartMenu

Length of output: 4121


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show relevant sections of ED64 low-level implementation
sed -n '1,260p' src/flashcart/ed64/ed64_ll.c | nl -ba | sed -n '120,240p'

# Find and show where ed64_writeback_save calls ed64_ll_get_sram / get_fram
rg -n "ed64_writeback_save|get_sram|get_fram" src/flashcart/ed64 -S

Repository: Polprzewodnikowy/N64FlashcartMenu

Length of output: 120


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show relevant portions of ed64_ll.c without relying on nl
sed -n '1,260p' src/flashcart/ed64/ed64_ll.c | sed -n '120,260p'

echo "---- caller search (ed64_writeback_save) ----"
rg -n "ed64_writeback_save" src/flashcart/ed64 -S

echo "---- show ed64_writeback_save function body ----"
# Print the function block by grabbing lines around the match and a bit more context
rg -n "ed64_writeback_save" src/flashcart/ed64 -n -S --context 15

echo "---- find where ed64_ll_get_sram is called ----"
rg -n "ed64_ll_get_sram" src/flashcart/ed64 -S --context 5

echo "---- find where ed64_ll_get_fram is called ----"
rg -n "ed64_ll_get_fram" src/flashcart/ed64 -S --context 5

Repository: Polprzewodnikowy/N64FlashcartMenu

Length of output: 11309


Add destination cache invalidation for ed64_ll_get_sram PI DMA reads

ed64_ll_get_sram() starts a PI DMA that writes into buffer (pi_dma_from_sram(buffer, ...)) but never invalidates the destination cache lines for buffer. When ed64.c loads saves from SRAM (the !is_fram_save_type / save_size > KiB(2) path), it calls ed64_ll_get_sram(cartsave_data, save_size) directly, so stale cached lines in cartsave_data can be used and corrupt what gets written to SD. The FRAM path already invalidates buffer before delegating to ed64_ll_get_sram, and pi_dma_read_data() follows the same “invalidate destination before DMA read” convention.

🛡️ Proposed fix
 void ed64_ll_get_sram (uint8_t *buffer, int size) {
     int offset = (size == (int) KiB(32)) ? 0 : SRAM_128K_OFFSET;

     pi_initialize_sram();
     dma_wait();
     pi_initialize();
     wait_ms(250);

+    data_cache_hit_writeback_invalidate(buffer, size);
     pi_dma_from_sram(buffer, offset, size);
     dma_wait();

     ed64_ll_set_sdcard_timing();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void ed64_ll_get_sram (uint8_t *buffer, int size) {
int offset = (size == (int) KiB(32)) ? 0 : SRAM_128K_OFFSET;
pi_initialize_sram();
dma_wait();
pi_initialize();
wait_ms(250);
pi_dma_from_sram(buffer, offset, size);
dma_wait();
ed64_ll_set_sdcard_timing();
}
void ed64_ll_get_sram (uint8_t *buffer, int size) {
int offset = (size == (int) KiB(32)) ? 0 : SRAM_128K_OFFSET;
pi_initialize_sram();
dma_wait();
pi_initialize();
wait_ms(250);
data_cache_hit_writeback_invalidate(buffer, size);
pi_dma_from_sram(buffer, offset, size);
dma_wait();
ed64_ll_set_sdcard_timing();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/flashcart/ed64/ed64_ll.c` around lines 150 - 162, ed64_ll_get_sram starts
a PI DMA into buffer via pi_dma_from_sram but does not invalidate the CPU data
cache for buffer, so stale cached lines can be read later; add a destination
cache invalidation (matching the convention used by pi_dma_read_data and the
FRAM path) immediately before calling pi_dma_from_sram in ed64_ll_get_sram
(e.g., call the platform cache-invalidate helper such as osInvalDCache or the
project's equivalent on buffer/size), keeping the rest of the sequence
(pi_initialize_sram, dma_wait, pi_initialize, wait_ms) intact and ensuring
ed64_ll_get_sram now invalidates buffer before the DMA read.

Comment thread src/flashcart/ed64/ed64.c
Comment on lines +86 to +117
int save_size = (int) file_get_size(current_state.last_save_path);
if (save_size <= 0 || save_size > (int) sizeof(cartsave_data)) {
return FLASHCART_ERR_LOAD;
}

ed64_ll_set_sdcard_timing();
if (ed64_apply_save_type(current_state.save_type) != FLASHCART_OK) {
return FLASHCART_ERR_LOAD;
}
wait_ms(250);

if (current_state.is_fram_save_type) {
ed64_ll_get_fram(cartsave_data, save_size);
current_state.is_fram_save_type = false;
} else if (save_size > (int) KiB(2)) {
ed64_ll_get_sram(cartsave_data, save_size);
} else {
ed64_ll_get_eeprom(cartsave_data, save_size);
}

if (f_open(&fil, strip_fs_prefix(current_state.last_save_path), FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
return FLASHCART_ERR_LOAD;
}

if (f_write(&fil, cartsave_data, save_size, &bw) != FR_OK || bw != (UINT) save_size) {
f_close(&fil);
return FLASHCART_ERR_LOAD;
}

if (f_close(&fil) != FR_OK) {
return FLASHCART_ERR_LOAD;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Writeback error paths leave is_expecting_save_writeback set.

If save_size validation fails (Lines 87‑89), ed64_apply_save_type fails (92‑94), or the file write fails (106‑117), the function returns FLASHCART_ERR_LOAD without clearing the flag or persisting state. Since ed64_init calls this on every boot when the flag is set, a persistently failing writeback blocks menu init on each reboot. Consider clearing/persisting state (or distinguishing transient vs. permanent failures) before returning.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/flashcart/ed64/ed64.c` around lines 86 - 117, The early-return error
paths in this block leave current_state.is_expecting_save_writeback set, causing
persistent boot failures; before each return FLASHCART_ERR_LOAD in the save
writeback flow (after save_size validation, after ed64_apply_save_type failure,
and on any file write/close failure) clear
current_state.is_expecting_save_writeback = false and persist that change (call
the existing state persistence routine, e.g. ed64_persist_state() or the
equivalent function used elsewhere) so the cleared flag is saved to disk; do
this immediately prior to each return to ensure the flag is not left set across
reboots.

@lewilou22

Copy link
Copy Markdown
Author

Some one with a ED64 V3 please test the save feature

@lewilou22 lewilou22 changed the title ED64 V2 Save Fix ED64 V Series Support May 31, 2026
@lewilou22

Copy link
Copy Markdown
Author

V2, V2.5 & V3 Confirmed to be working with saves

https://discord.com/channels/205520502922543113/1262536014623146125/1510786446099877999

@networkfusion

Copy link
Copy Markdown
Collaborator

Thanks for getting the saves working.

I would encourage the refactor to use the existing naming for v series carts
image
before it can be considered for a review.

@lewilou22 lewilou22 closed this by deleting the head repository Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants