This patch adds REU (Ram Expansion Unit) internal DRAM as monitor banks, so REU memory can be inspected and modified directly from the built-in monitor.
With this patch, when the REU is enabled, additional banks appear in the monitor's bank listing (e.g. reu00-07 for a 512K REU). All standard monitor memory commands work: m, >, f, h, c, etc.
This addresses feature request #481 (REU memory view) for the specific case of the REU cartridge.
This patch was developed with significant assistance from an AI coding agent (Claude, Anthropic). The AI helped with: codebase exploration, design decisions, writing C code, coding guidelines compliance, writing documentation updates, and drafting this submission text. All code was reviewed, tested, and validated by a human developer on real VICE builds. The design follows existing patterns already present in the VICE codebase (C128 dynamic bank lists, SCPU64 bank arrays, VDC bank precedent) -- these patterns were identified by reading the source code directly, not from AI training data.
The patch uses the existing bank array mechanism (MEM_BANK_ISARRAY / ISARRAYFIRST / ISARRAYLAST flags) already used by SCPU64 for its 256 RAM/ROM banks. No changes to the monitor code were needed -- it already handles bank arrays and displays them as collapsed ranges.
Three new accessor functions are added to reu.c/reu.h:
reu_get_size() -- returns current REU DRAM size in bytesreu_ram_read(addr) -- reads a byte from REU DRAMreu_ram_write(addr, value) -- writes a byte to REU DRAMEach machine's memory module (c64mem.c, c64memsc.c, c128mem.c) gets dynamic bank arrays that are lazily rebuilt when the REU enabled state or REU size changes. When the REU is disabled, the code falls back to the original static arrays with zero overhead. The bank read/peek/write/poke functions dispatch REU bank numbers to the new accessor functions.
For C128, the REU bank dispatch occurs before the existing 128-to-256 bank number translation, since REU bank numbers are the same regardless of the c128_full_banks setting.
bank command section), NEWS entry added
Unfortunately this doesn't follow the idea expressed in the comment of the future request - it is quite REU specific. We really want some kind of generic API that will work with other (all) cartridges, so we dont end up with dozens of special cases like this
Fair enough. It is however quite useful when developing stuff for the REU ;-)
It was unclear to me what the "more generic API" solution would look like, so I aimed at a simpler goal that was easier to achieve.
Would the goal of the generic API be specifically to add monitor support for accessing the memory on all different types of cartridges? Or do you have something else in mind?
If you can put a few words on what you are looking for I can see what I can do to achieve it.
Until then I will run this REU-specific solution locally. I was impressed that it could be achieved with quite limited modifications - so the architecture of the components this touches is sound. Maybe the more ambitious solution is within reach.
Last edit: Jesper Gravgaard 3 days ago
It should be able to cover all cartridge and internal expansions, on all emulated machines. It should support all kinds of monitor commands and features (see eg the "condition" command), and follow the same design pattern as with the other callbacks for cartridge things, so when a new kind of cartridge is added, only the respective hooks need to be implemented and called from the cartridge system (ie no changes to the monitor).
Edit: forgot: it should also handle more than one of those expansions being enabled at the same time
Last edit: gpz 2 days ago
Thanks for the feedback on the original patch and the design direction.
I have now built a proper generic API instead (still using Claude). Here's what I ended up with.
The idea
Rather than touching the mem modules or the monitor every time a cartridge wants
monitor visibility, cartridges now register themselves through a small central
registry (
cartridge_banks.h/cartridge_banks.c). The mem modules then rebuildtheir bank arrays on demand when the registry changes. No changes to monitor code
whatsoever -- it already handles everything via the existing
MEM_BANK_ISARRAY/MEM_BANK_ISARRAYFIRST/MEM_BANK_ISARRAYLASTflags.Registry design
An intrusive singly-linked list of
cart_bank_info_tnodes. Each node is a staticvariable owned by the cartridge -- the registry itself does zero heap allocation,
following the same pattern as
io_source_tincartio.h. Multiple expansions canbe registered and active at the same time; the linked list naturally holds all of
them and
rebuild_bank_arrays()interleaves their entries in registration order.Cartridges call
cartridge_bank_register(info)on enable or resize, andcartridge_bank_unregister(info)on detach. Registration is idempotent:re-registering an already-registered node just bumps the generation counter, which
is useful after a RAM resize without a full detach/re-attach cycle.
A
cartridge_bank_generation()counter increments on every change. Each machinemem module caches the last-seen generation and only rebuilds its dynamic bank arrays
when the counter differs -- so the common case (no cartridge change) costs a single
integer comparison per bank-system access.
Integration with the existing bank system
Each machine mem module (
c64mem.c,c64memsc.c,c128mem.c) now splits its bankarrays into a small static
base_bank*[]section (the fixed banks: default, cpu,ram, rom, io, cart) and a dynamically allocated
dyn_bank*[]section rebuilt fromthe registry when the generation counter changes.
rebuild_bank_arrays()iterates the registry, allocatesNUM_BASE_BANKS + total_cart_banks + 1entries, copies the base banks in, then foreach registered cartridge appends one entry per 64 KiB bank with a
snprintf-generated name (prefix + two hex digits), sets the appropriate arrayflags, and records
first_bank_numin the node somem_bank_read/write/peek/pokecan dispatch to the right cartridge's accessor.
Cartridge side
Each participating cartridge adds a static
cart_bank_info_tnode with a prefixstring, bank count, total size, and
read/writefunction pointers using a linearaddress convention:
(bank_index << 16) | page_offset. The write pointer may beNULL for read-only regions. Lifecycle hooks (
_enable,_disable,_powerup,_reset, resource change callbacks) callcartridge_bank_register/cartridge_bank_unregisterat the appropriate points.Wired up in this patch
Machines
mem_bank_read/write/peek/pokeCartridges
reu00..reuNN, where N depends on the configured size(128 KiB -> 2 banks, 16 MiB -> 256 banks)
geo00..geoNNAny cartridge registered via the API will automatically appear in all wired machines,
and any newly wired machine will automatically expose all registered cartridges.
Not wired yet (but the API is ready)
c64/cart/georam.cthat this patchmodifies -- wiring
vic20mem.cwith the dynamic bank system would expose VIC-20GeoRAM automatically with no further changes to
georam.cfit: RamCart (64-128 KiB), RamLink RAMCard (up to 16 MiB), REX RAM-Floppy
(256 KiB), DQBB (16-256 KiB), MMC Replay SRAM (512 KiB)
cpu/cartbank and don't benefit from separate entriesTesting
Manually tested on x64sc and x128 (GTK3 build, Linux and macOS) with REU and
GeoRAM active simultaneously at various sizes, including enabling/disabling and
resizing at runtime. Builds with zero warnings on both targets.