Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions src/rcheevos/consoleinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,29 @@ static const rc_memory_region_t _rc_memory_regions_nintendo_dsi[] = {
};
static const rc_memory_regions_t rc_memory_regions_nintendo_dsi = { _rc_memory_regions_nintendo_dsi, 2 };

/* ===== Nintendo 3DS ===== */
/* https://www.3dbrew.org/wiki/Memory_layout#ARM11_User-land_memory_regions */
static const rc_memory_region_t _rc_memory_regions_nintendo_3ds[] = {
{ 0x00000000U, 0x000FFFFFU, 0x00000000U, RC_MEMORY_TYPE_UNUSED, "" },
{ 0x00100000U, 0x03FFFFFFU, 0x00100000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Code Binary" }, /* Usual place where ExeFS .code is loaded */
{ 0x04000000U, 0x07FFFFFFU, 0x04000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "IPC Buffers" },
{ 0x08000000U, 0x0FFFFFFFU, 0x08000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Regular Heap and Stack" },
{ 0x10000000U, 0x13FFFFFFU, 0x10000000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "Shared Memory" },
{ 0x14000000U, 0x1BFFFFFFU, 0x14000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Linear Heap" }, /* Sometimes ExeFS .code is actually loaded here */
{ 0x1C000000U, 0x1E7FFFFFU, 0x1C000000U, RC_MEMORY_TYPE_UNUSED, "" },
{ 0x1E800000U, 0x1EBFFFFFU, 0x1E800000U, RC_MEMORY_TYPE_SYSTEM_RAM, "New 3DS Memory" },
{ 0x1EC00000U, 0x1EFFFFFFU, 0x1EC00000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "I/O Registers" },
{ 0x1F000000U, 0x1F5FFFFFU, 0x1F000000U, RC_MEMORY_TYPE_VIDEO_RAM, "VRAM" },
{ 0x1F600000U, 0x1FEFFFFFU, 0x1F600000U, RC_MEMORY_TYPE_UNUSED, "" },
{ 0x1FF00000U, 0x1FF7FFFFU, 0x1FF00000U, RC_MEMORY_TYPE_HARDWARE_CONTROLLER, "DSP Memory" },
{ 0x1FF80000U, 0x1FF81FFFU, 0x1FF80000U, RC_MEMORY_TYPE_READONLY, "Configuration Memory" },
{ 0x1FF82000U, 0x1FFFFFFFU, 0x1FF82000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Thread Local Storage" }, /* Most of this is actually unused in practice */
{ 0x20000000U, 0x2FFFFFFFU, 0x20000000U, RC_MEMORY_TYPE_UNUSED, "" },
{ 0x30000000U, 0x37FFFFFFU, 0x30000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "New Linear Heap" }, /* Newer games use this as the linear heap base instead */
{ 0x38000000U, 0x3FFFFFFFU, 0x38000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "New Linear Heap (New 3DS Exclusive)" } /* New 3DS exclusive space for the newer linear heap */
Copy link
Member

Choose a reason for hiding this comment

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

This map makes sense given the referenced documentation, but only these last two segments are the physical memory reported in the system specs. Where does all the other SYSTEM_RAM come from?

Copy link
Contributor Author

@CasualPokePlayer CasualPokePlayer Oct 13, 2025

Choose a reason for hiding this comment

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

I'm not sure what you mean here. To be clear, not all of the ranges will necessarily be addressed to actual memory (they would be mapped on the fly as allocations happen), not even neccessarily contiguous in their mapping, they're just the maximum ranges (assuming such memory is available in practice, in practice you'd OOM before you'd be able to do that). Most of the ranges here would possibly be addressed to some part of the 128/256MiB FCRAM (including things like IPC Buffers and Shared Memory, notwithstanding them marked as RC_MEMORY_TYPE_HARDWARE_CONTROLLER).

Copy link
Member

Choose a reason for hiding this comment

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

The hardware specs for 3DS say 128MB of RAM, and the hardware specs for New3DS say 256MB of RAM. I'm just confused where the data in all the other blocks marked as SYSTEM_RAM is being stored.

If it's just mapping data from the "New Linear Heap" segments, it could be marked as VIRTUAL_RAM instead of SYSTEM_RAM.

Ideally, we need some way to restrict the searchable memory space. Otherwise, when a dev captures memory to look for changes, they'll have to capture the full 750MB for the non-UNUSED memory blocks, and there could be redundant matches where memor is mirrored. Then, we would need some way to guide the developer into preferring one over an other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Again, not all of the range is necessarily going to be mapped, and there's no guarantee different mappings will be contiguous. Linear Heap allocations won't be overlapping with other memory allocations, so ranges where other memory has already been allocated will simply be "skipped" when an allocation occurs in order to find a suitable location for the allocation. For example, 0x30000000-0x30000FFF, being strictly mappable to FCRAM+0 to 0xFFF might not be able to be allocated for the linear heap if something allocated the FCRAM what would be there (noting that other memory regions, e.g. Code Binary and the Regular Heap, aren't strict about physical FCRAM locations), so it might proceed to look at 0x30001000-0x30001FFF, and so on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be clear too, when I say an area is "not mapped" that means that say, if the game were to read from that area on a console, it'd just segfault (data abort exception), although on emulator and for the purpose of peeking memory, 0s would be returned.

};
static const rc_memory_regions_t rc_memory_regions_nintendo_3ds = { _rc_memory_regions_nintendo_3ds, 17 };

/* ===== Oric ===== */
static const rc_memory_region_t _rc_memory_regions_oric[] = {
/* actual size depends on machine type - up to 64KB */
Expand Down Expand Up @@ -1133,6 +1156,9 @@ const rc_memory_regions_t* rc_console_memory_regions(uint32_t console_id)
case RC_CONSOLE_NINTENDO_DSI:
return &rc_memory_regions_nintendo_dsi;

case RC_CONSOLE_NINTENDO_3DS:
return &rc_memory_regions_nintendo_3ds;

case RC_CONSOLE_ORIC:
return &rc_memory_regions_oric;

Expand Down
1 change: 1 addition & 0 deletions test/rcheevos/test_consoleinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ void test_consoleinfo(void) {
TEST_PARAMS2(test_memory, RC_CONSOLE_NEO_GEO_CD, 0x010000);
TEST_PARAMS2(test_memory, RC_CONSOLE_NINTENDO, 0x010000);
TEST_PARAMS2(test_memory, RC_CONSOLE_NINTENDO_64, 0x800000);
TEST_PARAMS2(test_memory, RC_CONSOLE_NINTENDO_3DS, 0x40000000);
Copy link
Member

Choose a reason for hiding this comment

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

This will need to be updated to account for changes in #462.

  TEST_PARAMS3(test_memory, RC_CONSOLE_NINTENDO_3DS, 0x40000000, 0x2CE00000);

I'm a bit weary that there's still 750MB of searchable memory given the system only has 256MB of RAM.

The Code Binary segment, IPC Buffers segment, and Shared Memory segments are each 64MB. The Regular Heap and Linear Heap segments are each 128MB. There's also another ~48MB of miscellaneous sections that aren't the primary 256MB of RAM.

Would achievement developers be expected to find data in all of those regions? Anything that's not marked as UNUSED is currently searchable. Would it make sense to add new enum values for non-searchable system ram?

Maybe they could be marked as VIRTUAL_RAM and then have that made as non-searchable. Currently VIRTUAL_RAM is only being used for echo/mirror RAM segments, and eliminating those from search results would probably actually benefit developers on those systems.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Code Binary segment is something I would expect would be needed to looked at, as that doesn't just contain the .text, but also contains .data and .bss which are likely going to contain variables (and especially base pointers for fun pointer hell).

IPC Buffers and Shared Memory are unlikely to contain anything actually useful, as that's for handling communications with other 3DS processes (generally speaking this just means talking to the OS for various services, or in the case of emulators, the emulator HLE'ing those services). This is why those are listed as RC_MEMORY_TYPE_HARDWARE_CONTROLLER as they effectively work as "modern" MMIO (for unprivileged userland anyways).

TEST_PARAMS2(test_memory, RC_CONSOLE_NINTENDO_DS, 0x1004000);
TEST_PARAMS2(test_memory, RC_CONSOLE_NINTENDO_DSI, 0x1004000);
TEST_PARAMS2(test_memory, RC_CONSOLE_ORIC, 0x010000);
Expand Down
Loading