Menu

#425 REU monitor support

v3.x
open
nobody
None
enhancement
2 days ago
3 days ago
No

Summary

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.

AI Disclosure

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.

Machines affected

  • x64 (c64mem.c)
  • x64sc (c64memsc.c)
  • x128 (c128mem.c)

Technical approach

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 bytes
  • reu_ram_read(addr) -- reads a byte from REU DRAM
  • reu_ram_write(addr, value) -- writes a byte to REU DRAM

Each 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.

What's included

  • Code: 5 source files modified (reu.c, reu.h, c64mem.c, c64memsc.c, c128mem.c)
  • Documentation: vice.texi updated (REU section and bank command section), NEWS entry added
  • Coding guidelines followed (verified with checkstyle.sh -- no C++ comments, no tabs, no trailing whitespace, correct brace style)
  • Builds with zero errors and zero warnings
  • Tested manually on x64sc and x128 with 128K, 256K, and 512K REU configurations
1 Attachments

Discussion

  • gpz

    gpz - 3 days ago

    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

     
  • Jesper Gravgaard

    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
  • gpz

    gpz - 3 days ago

    Would the goal of the generic API be specifically to add monitor support for accessing the memory on all different types of cartridges?

    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
  • Jesper Gravgaard

    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 rebuild
    their 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_ISARRAYLAST flags.

    Registry design

    An intrusive singly-linked list of cart_bank_info_t nodes. Each node is a static
    variable owned by the cartridge -- the registry itself does zero heap allocation,
    following the same pattern as io_source_t in cartio.h. Multiple expansions can
    be 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, and
    cartridge_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 machine
    mem 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 bank
    arrays into a small static base_bank*[] section (the fixed banks: default, cpu,
    ram, rom, io, cart) and a dynamically allocated dyn_bank*[] section rebuilt from
    the registry when the generation counter changes.

    rebuild_bank_arrays() iterates the registry, allocates
    NUM_BASE_BANKS + total_cart_banks + 1 entries, copies the base banks in, then for
    each registered cartridge appends one entry per 64 KiB bank with a
    snprintf-generated name (prefix + two hex digits), sets the appropriate array
    flags, and records first_bank_num in the node so mem_bank_read/write/peek/poke
    can dispatch to the right cartridge's accessor.

    Cartridge side

    Each participating cartridge adds a static cart_bank_info_t node with a prefix
    string, bank count, total size, and read/write function pointers using a linear
    address convention: (bank_index << 16) | page_offset. The write pointer may be
    NULL for read-only regions. Lifecycle hooks (_enable, _disable, _powerup,
    _reset, resource change callbacks) call cartridge_bank_register /
    cartridge_bank_unregister at the appropriate points.

    Wired up in this patch

    Machines

    • x64, x64sc, x128 -- dynamic bank array rebuild and cart dispatch added to
      mem_bank_read/write/peek/poke

    Cartridges

    • REU -- reu00..reuNN, where N depends on the configured size
      (128 KiB -> 2 banks, 16 MiB -> 256 banks)
    • GeoRAM / NeoRAM / BBG -- geo00..geoNN

    Any 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)

    • VIC-20: GeoRAM already uses the shared c64/cart/georam.c that this patch
      modifies -- wiring vic20mem.c with the dynamic bank system would expose VIC-20
      GeoRAM automatically with no further changes to georam.c
    • Other C64 cartridges with significant externally-banked RAM that would be a natural
      fit: RamCart (64-128 KiB), RamLink RAMCard (up to 16 MiB), REX RAM-Floppy
      (256 KiB), DQBB (16-256 KiB), MMC Replay SRAM (512 KiB)
    • Small freeze-cart SRAMs (Action Replay, Expert, etc.) are already reachable via the
      cpu/cart bank and don't benefit from separate entries

    Testing

    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.

     

Log in to post a comment.

MongoDB Logo MongoDB