MJoergen / C64MEGA65

Commodore 64 core for the MEGA65 based on the MiSTer FPGA C64 core
GNU General Public License v3.0
23 stars 4 forks source link

.crt loading regression: Alienator/Canabalt work but EoB does not #108

Closed sy2002 closed 7 months ago

sy2002 commented 8 months ago

Presumably introduced in Alpha 3, which is based on commit c099921 and from 11/02/23

Observation with Alpha 3 on an R3 board:

a) loading and running .crt files: Canabalt Encore and Alienator load and start like a charme

b) A bunch of tested hardware cartridges work like a charme (we might need to dig deeper, if all known HW carts still work)

c) Eye of the Beholder: Loads, then a black screen for a second or so, then a reset and then back to BASIC.

The main difference between Canabalt and Alienator are: They are "small" and EoB is "big" (well: very big).

MJoergen commented 8 months ago

Using commit 7abc351 (from Oct 29): EoB works. This is just before the long-reset fix. Using commit f241136 (from Oct 30): EoB does not work. This is just after the long-reset fix.

To be continued ....

sy2002 commented 7 months ago

@MJoergen I probably will find time to debug this over the week-end; to avoid double-work, I am mentioning this here; you could un-assign yourself from this.

sy2002 commented 7 months ago

@MJoergen Here is what I found out so far and reminder: For the C64 to recognize and start a cartridge instead of going to BASIC, the signature CBM80 needs to be located from $8004..$8008, in PETSCII these are the values: C3 C2 CD 38 30. The KERNAL routine which does this check, reads backwards, i.e. from $8008 to $8004. So what we would expect when loading a cartridge is that after the CRT loading is completed, at some moment, the cart_roml_n signal goes low, indicating that the C64 reads in the cartridge area $8xxx.

And exactly this is what happens, when I load a "small" cartridge, for example Canabalt:

grafik

And this is why Alienator, Canabalt or other "small" cartridges are starting correctly.

What I also found out:

Even when loading a "big" cartridge such as EoB: There is never any spurious long reset being generated anywhere outside of main.vhd and no spurious hard_reset_n being generated inside main.vhd. The latter one would mask the "CBM80" signature as written above. So if the C64 core - also when loading a "big" cartridge such as EoB - wants to access the cartridge memory, it always can.

And here is the puzzle:

When loading a big cartridge, the C64 core is reset in a way or behaves in a way that it never ever generates any cart_roml_l signal at all: It seems not to run through the cold start init sequence.

Another (minor? or irrelevant?) thing I discovered:

I don't think that this has anything to do with the phenomenon but I will test it later:

Reminder: The agreed upon semantics (which is not documented anywhere, note-to-self to fix this soon): Within the main.vhd of the Commodore 64 core we are not using the reset signals that come from "outside", but stick to our internal, C64 core-specific, reset logic of hard and soft resets and what those "mean".

Technically this means: There are two reset signals generated in the hard_reset process: reset_core_n is a soft reset of the core and hard_reset_n is a long reset of the core. All components in main.vhd are adhering to this semantics but two are not: i_reu_mapper and i_avm_cache are using the outside-generated i_reset_soft instead of the inside-generated reset_core_n which means that these resets are out of sync.

sy2002 commented 7 months ago

The deeper I am diving, the more a start to be convinced that our old implementation in V5 only worked by chance and that fixing the long reset bug is uncovering deeper, more complex issues. Next puzzle piece is from fpga64_sid_iec.vhd (MiSTer file):

process(clk32)
begin
    if rising_edge(clk32) then
        if preCycle = sysCycleDef'high then
            reset <= not reset_n;
        end if;
    end if;
end process;

sysCycleDef is a custom type used for the state machine and consists of 32 states that are cycled through, one per rising core clock. reset_n is the input signal to the MiSTer core and reset is a signal that the core internally uses.

This code basically tells us: If the incoming reset signal is too short, then bad things will happen. We never took this into consideration so far.

MJoergen commented 7 months ago

Great analysis!

  1. I seem to remember having problems with resetting the C64 core, probably in conjunction with the SIMCRT support. Indeed, the code snippet shows that the reset must be asserted for (at least) 32 clock cycles.
  2. The fact that i_reu_mapper and i_avm_cache are not adhering to the overall reset semantics seems wrong. I know who wrote that part, and I'm pretty sure he didn't consider this. I'm pretty sure they should both use the soft reset, i.e. reset_core_n
sy2002 commented 7 months ago

@MJoergen Thank you for your feedback. Here is the next finding:

1) Canabalt (which works), is a 16KB Cartridge and according to this documentation does what is expected: One clock cycle after reset_core_n is de-asserted (goes back to 1), we see both, exrom and game (both low-active, too) going to zero. This is what a so called "16 KB Cartridge" is supposed to do:

grafik

2) Eye of the Beholder (EoB), which does not work, is a complex cartridge with bank switching (I assume it is in EasyFlash Format but did not check that yet). exrom and game should do something - but they are not:

grafik

This is the reason, why - as described in the comment above (https://github.com/MJoergen/C64MEGA65/issues/108#issuecomment-1806798071) "behaves in a way that it never ever generates any cart_roml_l signal at all".

In main.vhd we have:

         -- Simulated cartridge using data from .crt file
         when 2 =>
            core_game_n    <= crt_game;
            core_exrom_n   <= crt_exrom;

And crt_game and crt_exrom are outputs of i_cartridge. So my next step is now to instrument i_cartridge and see what happens there when EoB is loaded. Stay tuned - I'll keep you posted.

MJoergen commented 7 months ago

My God! This is a real cliff-hanger!!!

I'm eagerly and curiously waiting for the resolution of this bug. The suspense is driving me crazy!

sy2002 commented 7 months ago

@MJoergen And here is the resolution of the cliff-hanger:

When I fixed the long reset bug, I also changed the reset input for i_cartridge - see screenshot of commit. This was a mistake:

grafik

From hard-reset to soft-reset. This is the wrong semantics: The component sets the correct exrom_o and game_o while cart_loading_i='1'. During a reset signal via "rst_i" exrom_o, game_o and other stateful signals are reset to the neutral state. Due to the fact, that sw_cartridge_wrapper uses a soft reset to make sure the C64 starts the cartridge, we must not reset i_cartridge on soft reset.

The commit https://github.com/MJoergen/C64MEGA65/commit/17f06ab8869110869c66d906724d929388b1c408 fixes the issue and I also seized the opportunity to document the C64 core's (very core-specific) reset semantics in main.vhd.