Menu

#155 Trilogic Expert Cartridge support for Denise

closed
PiCiJi
None
C64
4 days ago
2026-07-27
ClausS
No

This patch adds support for the Trilogic Expert Cartridge (CRT hardware type 6) to Denise.

The implementation follows the original hardware behaviour:

8 KiB writable cartridge RAM
OFF / PRG / ON switch modes
Freeze support
Correct IO1 latch behaviour
Disk-loaded Expert software modules
Proper Soft Reset, Hard Reset and Master Reset handling

Unlike most cartridges, the Expert contains RAM only. The CHIP data stored in the CRT file is therefore used as the initial contents of the cartridge RAM rather than as immutable ROM.

To make the cartridge easy to use, Denise has been extended with Expert-specific controls.

The cartridge configuration page provides the physical ON/OFF and PRG switch positions.

When an Expert Cartridge is attached, the main C64 menu is also extended with Expert-specific entries:

Expert Master Reset
Expert Cartridge Button (OFF / ON / PRG)

These controls are available only while an Expert Cartridge is active.

Expert Master Reset intentionally discards any software module loaded into Expert RAM and restores the original RAM contents from the attached CRT image. This complements the modified Soft Reset and Hard Reset behaviour, which preserve disk-loaded Expert modules and their working state.

A small compatibility fix was also added for legacy Expert CRT images that contain an incorrect CHIP packet length. Denise now accepts these images in the same way as real hardware and other emulators.

The implementation has been tested with several original Expert software modules, including software that requires the original OFF → PRG → ON → Soft Reset workflow.

The patch was created against Denise origin/master commit 7b11d17e and contains only the Expert Cartridge implementation.

Claus

2 Attachments

Discussion

  • Anonymous

    Anonymous - 2026-07-27

    Very nice! Claus, can you please add support of the "Diashow Maker" cartridge too? That would be great. I own this cartridge since the 80's and I really like it, for making display-pictures, out of pics, that I freezed from demos or games. This cartridge doesn't have a particularly good freezer, but it offers good options, for saving frozen images.

     
    • ClausS

      ClausS - 2026-07-29

      Dear Anonymous,
      Unfortunately, I don't own this cartridge, so I can't test it. I could look at how it's implemented in VICE, but without the real hardware or a corresponding CRT file, I wouldn't be able to verify that it works correctly. Therefore, I'm not able to add support for it.

       
      • Anonymous

        Anonymous - 2026-07-29

        Hy Claus. Here's a crt-image of the "Diashow Maker".

         
        • PiCiJi

          PiCiJi - 2026-07-29

          @Anonymous
          Please create a new ticket for this. This makes it easier to keep track of things.

           
        • ClausS

          ClausS - 2026-07-30

          Diashow Maker has already been supported by Denise since version 2.1, released in September 2023.
          Commit: 0f98ef15c6db9f7d1379021cb38ea04dbce8bd36
          add carts diashow maker and super snapshot v5

          Therefore, no new integration is required.

          So please, no further follow up here.

           
  • PiCiJi

    PiCiJi - 2026-07-28

    thanks,
    will add it when I am back at home.

     
    • ClausS

      ClausS - 2026-07-28

      I apologize, but I accidentally provided an incorrect version of the Expert Cartridge patch.
      That version added a separate “Expert Cartridge” media section, although the cartridge already belongs in Denise’s existing “Freezer” section. This resulted in a redundant and confusing not correct user-interface entry.
      I have corrected the implementation and replaced the patch. The Expert CRT is loaded through the existing Freezer section and is automatically recognized as CRT type 6. The OFF/ON/PRG switch and all Expert Cartridge functions remain available.
      The corrected patch is:
      denise-expert-cartridge-upstream.patch
      The updated patch contains no test-framework, build-system, COMAL, or HybridCart changes.
      I am sorry for the confusion and the additional work this may have caused. Please disregard the previous patch and use the corrected version instead.

       
      • PiCiJi

        PiCiJi - 2026-07-29

        No problem, it will be a few more days before I'm home.

         
  • PiCiJi

    PiCiJi - 2026-08-01

    For now, I have only looked at the integration outside the actual CRT implementation. It is important to me that the code remains as generic as possible so as not to lose the big picture from a developer's perspective. I am aware that generalization can compromise usability in certain areas if you try to force things to fit the existing concepts.
    However, a less significant card like this shouldn't generate as many exceptions as e.g. SuperCPU.
    Sometimes, you have to relax the concept (but for all cards) to accommodate new scenarios.
    Since this usually requires rethinking more than just one's own code, there really ought to be a very good reason for it. (don't see it for this card)

    The card is too specialized for the "Freezer" category. Instead, a separate entry should be created for it—similar to the REU, SuperCPU, or RetroReplay (which also includes a freezer function). I suspect your initial idea was along those lines, though I didn't look at the old patch.
    In this way, the automatic display of the defined jumpers is available in the UI.

    The card's controls should be handled via jumper logic (similar to the RetroReplay) rather than being toggled within the C64 menu. This ensures users don't have to adjust their workflow.
    While a radio-button style setup might be preferable for the "On, Off, PRG" modes, using two jumpers is good enough to avoid the need to create a special case for this exception. This results in four possible states instead of three—with some redundancy—but it is an acceptable trade-off.
    As I understand it, there are two push-buttons: Freezer and Master Reset. The existing "customCartButton" logic within the C64 menu should be used for the Master Reset function.
    At some point, I’ll update the "customCartButton" menu entry so it reflects the specific module rather than just being labeled "cartridge button" besides "freeze button"

    The API interface should be as generic as possible to make it easier for interested parties to get started.
    prepareExpertHardReset or masterResetExpert are not suitable for this purpose.
    If I understand correctly, the existing "customCartridgeButton" should suffice for the master reset.
    A hard reset is essentially "pulling the plug," and the cartridge SRAM cannot retain data without a battery.
    If the SRAM has a battery, a write operation to the associated file would need to be triggered via setrom(media, nullptr) from the ExpansionPort interface. The "powerOff" function in the UI triggers setrom(media, nullptr).

    No variables from the "Expert" class should be stored in the "system" class. Otherwise, it becomes difficult to keep track of things when making global changes.

    If I’ve overlooked any functionality, let me know.

     

    Last edit: PiCiJi 2026-08-01
    • ClausS

      ClausS - 2026-08-02

      Hi,

      thank you for your review and for explaining the reasoning behind your comments.

      In the beginning, my implementation also used a separat Expert Cartridge section. During coding i had other ideas, and moved it into the existing Freezer category.
      Maybe I was mainly looking at it from my perspective.

      After i read your explanation about keeping the architecture as generic as possible and maintaining the developer's perspective, I understand what you mean. And it makes sense.

      Give me some time, for adjusting it.
      I will put back the Expert Cartridge as its own expansion instead of placing it in the Freezer category,
      Also use the generic jumper mechanism for the ON/PRG switch, and the existing Custom Cartridge Button as the Expert Master Reset.

      There is one point where I am not completely sure about the intended behaviour:
      Hard Reset.

      On the real hardware, the ON/OFF/PRG switch is a physical switch and therefore keeps its position across a power cycle.

      A typical workflow is:
      load an Expert module from disk,
      switch to PRG,
      then to ON,
      use the freezer,
      power the C64 off.

      When the C64 is powered on again, the Expert RAM has lost its contents, but the switch is still physically in the ON position. As a result, the C64 will no longer boot correctly until the user notices the switch position and moves it back to OFF.

      For the emulator I can see two reasonable interpretations:

      Option 1
      Treat a Hard Reset as a power cycle and automatically switch the Expert cartridge to OFF.

      Option 2
      Leave the switch position unchanged, but automatically reload the Expert RAM from the currently inserted CRT image, restoring the cartridge to the state represented by the inserted CRT.

      How do you think about this?

      Claus

       
  • PiCiJi

    PiCiJi - 2026-08-02

    Hi,
    In Denise, a hard/soft reset should not alter or reset any cartridge jumpers. (just like with real hardware)
    When the emulator is hard reset, the current(latest) jumper settings, shown in UI, are imported into the respective expansions; they should not subsequently be altered later by the cartridge's reset function. (otherwise the UI wouldn't match cart jumper state )

    Jumpers are only changed by the user or by loading a save state—to ensure the jumper configuration matches the save state. When a save state is loaded, the carts jumper state is updated, but not the UI. It is therefore possible that, when loading a save state, the jumper setting shown in the UI does not match the internal setting on the cartridge. The advantage is that, upon the next hard reset, the jumper setting visible in the UI is written back to the cartridge. The downside is that it can be confusing for the user if they have to adjust the jumpers after loading a save state; for instance, the cartridge's jumper might be off while the UI shows it as active, requiring the user to toggle the jumper again. However, save states are generally not used during critical operations like programming an Expert Cart or flashing an EasyFlash. I might change this behavior at some point in the future.
    have a look at RetroReplay: both jumpers are not changed during reset but saved in
    "auto RetroReplay::serialize(Emulator::Serializer& s) -> void {"

    so Option 2:

    The Expert Cart will likely require a new row in the UI. That is not a problem for now. Once the card has been added, I will redistribute the final section, "Accuracy and Performance."

     

    Last edit: PiCiJi 2026-08-02
  • ClausS

    ClausS - 2026-08-03

    I have now reworked the patch according to your recommendations.

    The Expert Cartridge is now integrated as its own expansion, uses the generic jumper mechanism, the generic Custom Cartridge Button, and keeps all Expert-specific state inside the Expert class. I think it follows now the existing Denise architecture.

    The updated patch is attached.

    Claus

     
  • PiCiJi

    PiCiJi - 2026-08-03

    Thanks for the changes.
    I created a git branch called "trilogic_expert".
    I moved the Expert class out of the Freezer folder and placed it—as .cpp and .h files—into a new folder. freezer.cpp should only handle the classes listed in the Freezer menu. Like GameCart, Freezer acts as a container for similar cards; Trilogic is therefore completely independent.
    I also added some translations for UI
    testing: I couldn't find the CRT right away.
    I'll wait for any further adjustments from you. please create further patches for this cart on "trilogic_expert" branch. (as long as the branch isn't in master)

    Modifying the memory map:
    system->memoryCpu.map + system->remapCpu() could cause side effects. I would only do this if emulating the actual behavior leads to slowdowns. I encountered this issue with EasyFlash3 and used a similar hack.
    You mentioned that GAME is determined by the address on the bus (which the cartridge can see). There is a virtual "clock" function in the ExpansionPort class.
    This executes every cycle prior to any read/write access.
    You could monitor the CPU's address bus there and, upon changes, update via system->changeExpansionPortMemoryMode(exRom, game);.
    For the REU, 'clock' function is in use and then checks: system->cpu.isWriteCycle() / system->cpu.addressBus().
    Please check this out, if you haven't already.

    Loading save games:
    Forget what I wrote about this earlier. When loading a save game, the jumpers in the UI are updated. I thought that was still a "to-do" .

    Display of expansions in the system UI.
    It looks a bit messy at the moment; currently, five expansions are displayed per row.
    Unfortunately, I took a shortcut and didn't account for the possibility of reordering the expansions later. when rearranging the expansions in Interface::prepareExpansions(), it causes major issues because the expansions array is accessed via the expansion ID—a pattern that runs throughout the entire app. Once you’ve finished your changes, I’ll refactor this globally to allow for reordering expansions at any time, ensuring the rows in the UI are of relatively uniform length.

     

    Last edit: PiCiJi 2026-08-03
    • ClausS

      ClausS - 2026-08-05

      Sorry for late replay, i was a little bit busy.
      here is the patch according your request.
      I did some tests, and could not find any problems.
      Attached you can find also a Trilogic Disk, and 2 .crt Files.
      Claus

       
      • PiCiJi

        PiCiJi - 2026-08-06

        Thanks.
        The expert branch is now live. could you have a final check please.
        I like the fact that the cartridge monitors the address currently on the BUS and uses that to control the game.
        I set isBootable() to false for the cartridge. This function is designed to deactivate a game cartridge when a disk, tape, or PRG image is double-clicked, auto-started, or dragged and dropped.
        However, for cartridges that do not boot upon a hard reset, it is better if the cartridge doesn't become unplugged—for instance, when a disk image auto-starts. Otherwise, you would only be able to insert Expert.d64 and load it manually.

        because the name was confusing, I renamed the global cartridge function rebuild() to build() and because of this updated Expert::assign, create and setRom
        Individual cartridges like RetroReplay or Trilogic Expert are instantiated only once in system/expansion.cpp.
        Only cartridges in the "Freezer" or "GameCart" categories are destroyed and recreated when switching. I need to simplify the process.

        edit: There is currently no suitable location for "EXPERT CARTRIDGE GUIDE.md".
        A hyperlink widget already exists that could, for instance, be placed directly under Denise->Software->Trilogic. The problem is that free hosting providers tend to remove files after a certain period. Perhaps that has changed.

         

        Last edit: PiCiJi 2026-08-06
        • ClausS

          ClausS - 2026-08-07

          Hi,
          while doing some tests, I noticed that Expert::setRom() calls build(), but not prepare(). As far as I understand the code, this means the CRT is recognized, but its CHIP data is not copied into expertRam.
          Was this behavior intentional? I only want to make sure that I understand the intended behavior correctly.
          Personally, I would find it useful to be able to load a pre-filled Expert image. It makes the cartridge more practical to use because the Expert software does not have to be loaded again from disk after every hard reset. Otherwise, this would also require switching from the currently inserted game disk to the Expert software disk and back again.

          Claus

           
  • PiCiJi

    PiCiJi - 2026-08-06
    • status: open --> feedback
     
    • PiCiJi

      PiCiJi - 2026-08-07

      You're right about that. I forgot that. It should work now.

       
      • ClausS

        ClausS - 2026-08-08

        Thanks for implementing the Trilogic Expert Cartridge support
        I have now tested it on both macOS and Windows, and everything seems to be working fine.
        Thanks for working together with me on this topic.

        Claus

         
        👍
        1
  • Anonymous

    Anonymous - 2026-08-16

    Since the cartridge has 1 tri-state switch PRG/OFF/ON, it is impossible to have both ON and PRG simultaneously activated, which is possible in the emulator. The way it could be solved is that if one of them is activated and the user wants to activate the other one, then the activated one is deactivated automatically.

    Tom

     
    • ClausS

      ClausS - 2026-08-17

      You're right that on the real cartridge, ON and PRG cannot be selected at the same time because it is a three-position switch.
      However, this has no functional effect in Denise. PRG has priority over ON, so if both are enabled, the cartridge is simply in PRG mode. ON is ignored until PRG is disabled again.
      So this is only a visual inconsistency in the UI; it does not create an additional or incorrect operating mode.

      This was discussed during the integration, and the two generic switches were kept to avoid introducing a special three-position control just for the Expert Cartridge.

      Claus

       
      • Anonymous

        Anonymous - 2026-08-17

        I've never had such a Trilogic Expert cartridge, but couldn't that 'problem' be solved by graying-out the relevant functions in such a moment? It would be a bit strange, if the user could set something in the emulator, that isn't possible with the real hardware (at least that's what I think in this point).

        Cheers,
        G.I. Joe

         
  • PiCiJi

    PiCiJi - 4 days ago
    • status: feedback --> closed
     

Anonymous
Anonymous

Add attachments
Cancel