Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MEGA65 cart support #62

Closed
dansanderson opened this issue Jul 1, 2023 · 22 comments
Closed

MEGA65 cart support #62

dansanderson opened this issue Jul 1, 2023 · 22 comments
Labels
enhancement New feature or request

Comments

@dansanderson
Copy link
Collaborator

This issue is to track the ROM side of implementing a new standard for MEGA65 cartridges. The next core will have the plumbing for implementing physical cartridges with a MEGA65 ID, so a cart can contain a MEGA65 program and start the MEGA65 core (vs. C64 carts starting the C64 core). The MEGA65 ROM needs the ability to detect a connected cartridge and the MEGA65 ID, and automatically start the program from the MEGA65 boot state.

There should be existing code that detects C64 carts and activates the MEGA65 C64 mode. We should retain this for when the MEGA65 is configured to start the MEGA65 core for C64 cartridges (the default behavior when no C64 core is installed, and configurable by the user with the new core in slot 0).

@lydon42 please describe the MEGA65 cart spec that you've decided on to this ticket. Also please attach your test cart image. (I have EF1 hardware I can put it on.) ty!

@dansanderson dansanderson added new New report, not classified yet enhancement New feature or request and removed new New report, not classified yet labels Jul 1, 2023
@lydon42
Copy link
Member

lydon42 commented Jul 1, 2023

Cartridge ROM is at $400.XXXX. So a standard ROMLO cartridge can be found at $400.8000.

A M65 cartridge has the bytes $4D $36, $34 ("M65") at $400.8007. This is like a C128 cartridge.

I use

EDMA 0, $2000, $4008000, $8000
BANK0:SYS$8000

to copy and excute ROMLO of my testcartidge.

Current idea is that the ROM does MAP $8000-$9FFF and then JMP $8000 to execute the cartridge coldstart.

Cartridge files are on discord, github does not allow the 7z

@lydon42
Copy link
Member

lydon42 commented Jul 2, 2023

Optional: slot 0 currently does not care about EXROM/GAME. If they are low in any combination, but M65 is found at 8007, the ROM should assume that this is a MEGA65 cartridge and exit C64 mode, initialize MEGA65 mode and than map & start.
This is untrue for Ultimax mode, obviously, as this will shadow the ROM, so we need only take car for this in the other two EXROM/GAME combinations.
Which means that the C64 ROM actually would also need to be aware of this, as C64 starts first, right?

Resources:

@dansanderson
Copy link
Collaborator Author

dansanderson commented Jul 12, 2023

The current start-up sequence checks for "CBM80" (MSB=1 for the letters) at 8004-8009 with the default C64-style memory configuration without bothering to check GAME/EXROM. If found, it assumes a C64 cart and does jmp ($8000). If not found, it maps M65 kernel char ROM via D030 and starts MEGA65 (jmp $C800). Even though the C64 kernel gets to be the reset vector for system start-up, the only way it doesn't immediately map in the M65 kernel is if a C64 cart is detected. The "hold Mega key for GO64" is handled in the M65 kernel start and invokes the GO64 procedure to return to a C64 configuration.

The new version will leave the C64 side of start-up intact, because a M65 cart will not register as a C64 cart and start-up will pass through to the M65 kernel. M65 start will check for "M65" (MSB=0, similar to C128) at $4008007 and bit 0 set at cart flags $4008006, and if found will map $4008000-9FFF to $8000-9FFF and do jmp $8000 (not jmp ($8000)). Similarly, M65 NMI's RunStop-Restore handler will do the same check, redo the mapping, and jmp $8003.

For now I'll assume:

  1. If a M65 cart is present, this disables the ability for the user to hold the Mega key for C64 mode or RunStop or the M65 monitor. (The cart check will happen before these other start-up checks.)
  2. If cart flag bit 0 is set, map cart memory and jump to cold start during boot, and to warm start during RunStop-Restore.
  3. If cart flag bit 0 is clear, start BASIC normally and do not map cart memory. A non-booting cart will need to be mapped into lower memory explicitly by whomever wants to use it.
  4. Other cart flags don't do anything yet. There is no 01/FF distinction as there is for the C128. Bit 0 will always indicate whether to jump to the cold start vector during start-up as described, and we'll tell everyone to leave the other bits clear for now for future expansion. (If we want to replicate the 01/FF distinction from the C128 I'll need more information.)
  5. There is no reason to test EXROM/GAME from the kernel code. This has the C64-style effects on memory configuration for 0/0, 0/1, and 1/0, and no effect for 1/1.

Let me know what you think! I'll prepare this change and send you a test ROM. I'll also try to prepare an EF1 cart with the test cart data.

@dansanderson
Copy link
Collaborator Author

dansanderson commented Jul 12, 2023

@lydon42 @ki-bo Check my thinking:

To map $8000-$9FFF to $400.8000, I need to set the MAPHI offset to $400.0000. All upper 16-bit addresses must use the same offset, so with this MAP the upper 8 KB segments either refer to cart ROM or bank 0 RAM. M65 kernel ROM—including hardware vectors—is mapped in with MAP, and so cannot be mapped to 16-bit addresses at the same time as cart ROM.

Previously we've had D030 override MAP so bit 7 could preserve kernel in E000-FFFF. However, I understand that we're changing this: MEGA65/mega65-core#712

Therefore, a M65 cartridge cannot coexist in lower memory addresses with kernel ROM, must set the FFFx hardware vectors, and therefore must be 32 KB (or at least electronically return values for the hardware vectors) and have all 32 KB of $8000-$FFFF mapped to $400.8000-$400.FFFF. If a cart wants to use the kernel, it has to manage memory on its own, either by copying kernel code to bank 0 or by copying its own code to bank 0, and setting the MAP register.

Am I thinking about this correctly? Is it just expected that a typical cart will have a start-up routine that DMAs itself into bank 0? Does this influence any of our other decisions regarding memory management?

@ki-bo
Copy link
Member

ki-bo commented Jul 12, 2023

I think you are correct. How about mirroring the kernel ROM behind the cart ROM so it could be made available with the same offset?

@lydon42
Copy link
Member

lydon42 commented Jul 12, 2023

First some thoughts:

  • I was not aware the mega-byte mapping is in 32kb chunks...
  • the EF1CR is not 32KB it is only 16KB
  • to map cartridge ROM to the high kernel space would mean to use ultimax mode (which is different kinds of awkward)

So the other plausible way to do it would be to not MAP $8000, but to do the DMA copy.

Or a core based solution would be the ability to fake EXROM/GAME to force the mapping.

@dansanderson
Copy link
Collaborator Author

dansanderson commented Jul 12, 2023

Sorry, I may have overlooked an obvious solution: leave MAP disabled for E000-FFFF, and use D030 mapping for ROME. This already has to happen with ROMC, where the boot code lives. ROMC does the MAP'ing, so it cannot be MAP'ed and survive the process. (As the manual cautions, doing a megabyte MAP disables MAP'ing partway through the process, so the code doing the MAP'ing must not depend on MAP to stay in memory.)

The M65 default (non-cart) boot state has D030 bit 7 (ROME) clear, and MAPHI B300 = +3.0000 for %1011 ($8000-BFFF, $E000-FFFF). Maybe it's sufficient for cart boot to set ROME, set MAPHI to +400.0000 for %0011 ($8000-BFFF), then let the cart boot code figure out what else it needs to do—including possibly setting MAP again to make more hardware addresses visible in the 16-bit address space.

(I don't know why the M65 kernel uses MAP and not ROME to map itself to E000-FFFF. Maybe there's a reason I'm not thinking of. It's very late in my timezone rn. :) )

@lydon42
Copy link
Member

lydon42 commented Jul 12, 2023

This sounds like a solution to me...

In my mind a big cartridge (say Showdown 65 as a cart) would have a loader stub in $8000, which will copy itself downward into RAM, and then begin to map different cartridge pages (in that case using EF1CR registers to set EXROM/GAME to map 16K into $8000-BFFF) and DMA them into memory banks and/or attic RAM, replacing the loader that would do this from disk.

@ki-bo
Copy link
Member

ki-bo commented Jul 12, 2023

But shouldn't we assume $D030.7 mapping C64 Kernal? That's what the C65 is doing. And this is the reason it is not used but MAP instead for mapping the Kernal at $E000.

@lydon42
Copy link
Member

lydon42 commented Jul 12, 2023

If that is what the C65 is doing, then this is what we should do!

@dansanderson
Copy link
Collaborator Author

Interesting! It's not what the MEGA65 has been doing as far back as 920287. Just after boot, PEEK($D030) returns $64 and MAPHI is B300. We've also been giving D030 priority over MAP in the core until the recent proposed core change, dunno if that has anything to do with it.

Since the very beginning of our own source repo (version 920108, January 2021), kernel64.src:c65_mode sets D030 to %00100000, which is ROMC only. Something else must change this to $64 later, but that still leaves ROME clear. MAPHI B300 MAPLO E300 is set in system.src:start today at least as far back as 920276 and is still set that way when I boot with older ROMs.

@dansanderson
Copy link
Collaborator Author

Hmm, ROM 910828 also boots with D030 set to $64 and MAPHI B300 MAPLO E300. Does ?PEEK(53296) show something other than 100 on a real C65?

@ki-bo
Copy link
Member

ki-bo commented Jul 12, 2023

I think this is ROM dependent. Starting up an old Commodore ROM should give you the C65 value for D030.

$64 basically means to enable only ROMC mapping. The other bit in the upper nibble just switches the char rom. And I think this bit is actually ignored at the moment in the M65 core. So, no difference between $2x and $6x.

@ki-bo
Copy link
Member

ki-bo commented Jul 12, 2023

It makes sense to only map $2Cxxx via $D030 in C65 or M65 mode, as all other mapping bits of $D030 will also map in $2xxxx blocks. Getting $3xxxx ROM area mapped needs MAP functionality.

@ki-bo
Copy link
Member

ki-bo commented Jul 12, 2023

First some thoughts:

  • I was not aware the mega-byte mapping is in 32kb chunks...

The limitation of having only two offsets for mapping are already present in the regular MAP function (when not mapping beyond the 1MB limit). You have one offset for all addresses up to $7FFF and a second offset for the upper 32kb range. But you can decide for each 8k block whether the offset gets applied or not. Offsets can have a granularity of 256 bytes.

The MB mapping just extends the value range of the offset parameter to also reach address space >1MB.

@dansanderson
Copy link
Collaborator Author

@ki-bo We've documented D030 as accessing ROM8, ROMA, and ROME from 3.xxxx, and ROMC and CROM9 from 2.xxxx. Is that inaccurate? Is it an inaccuracy in our implementation (doesn't match C65) or our documentation?

As mentioned, Commodore ROM 910828 sets D030 to $64. I concur that this leaves ROME unset, which is why I concluded that ROME is brought in by MAP. Are you seeing something different on a C65? I had thought you were suggesting that a C65 brings in ROM E000-FFFF via D030 and not via MAP.

@ki-bo
Copy link
Member

ki-bo commented Jul 13, 2023

Our implementation does not match what a real C65 is doing. You can read the difference in mapping in this issue:

MEGA65/mega65-core#712

$D030 is a VIC-III register controlling access to a specific 32kb address range in ROM. The reason why C65 Kernal is banked in via MAP and not via $D030 is that it is not possible to do it that way with a real VIC-III chip. Because of this, it is easy to correct now on M65 as most software is not yet relying on $D030 flags.

@dansanderson
Copy link
Collaborator Author

OK, got it. I overlooked that part of the memory mapping proposal. With the proposal in place, D030 can never map M65 ROM data. (It can and must init D030 to pull in ROMC from the C64 kernel before the reset vector is called by the hardware.)

To get cart data accessible at 8000-9FFF and M65 ROM at E000-FFFF, we either 1) DMA one and MAP the other, 2) DMA both, or 3) somehow make them both visible at the same offset so they can both be MAP'd. #3 sounds like more trouble than it's worth, and #2 seems unnecessary. DMA'ing the cart addresses makes more sense than DMA'ing the kernel ROM. It's more consistent with non-cart behavior, and doesn't meaningfully rule out any fancy expansion port possibilities (e.g. dynamic values). The cart just needs to reliably serve its header and bootstrap routine to DMA, both during cold boot and during warm boot (RunStop-Restore).

I'll proceed this way. Thanks both! Let me know if you think of anything else.

@ki-bo
Copy link
Member

ki-bo commented Jul 13, 2023

OK, got it. I overlooked that part of the memory mapping proposal. With the proposal in place, D030 can never map M65 ROM data. (It can and must init D030 to pull in ROMC from the C64 kernel before the reset vector is called by the hardware.)

Actually, on a real C65, I wouldn't be surprised if $D030 is not initialized in HW during reset, essentially doing no mapping at all, not even ROMC nor ROME. Instead, probably register $01 is initialized in HW to map in C64 Kernal, in order to provide reset vector and the reset routine.

@dansanderson
Copy link
Collaborator Author

Correct, I meant the C64 kernel sets D030 in its reset vector routine, which starts at $E4B8. It sets D030 then jumps to $C800 in the M65 ROM. 👍

@johnwayner
Copy link

johnwayner commented Dec 3, 2023

@dansanderson With the latest beta ROM and core from 2023-11-23, I'm able to boot lydon's test cartridge using my Ultimate II+, so this appears to be implemented. What's left to do for this task?

@dansanderson
Copy link
Collaborator Author

The MEGA65 ROM part of the proposed MEGA65 cartridge auto-boot protocol is complete. I made a note to figure out whether the cartridge warm boot vector gets called in a way that's consistent with expectations (Run-Stop/Restore? I don't actually know), but other than that, it supports the one test cart.

I left this open mostly because I wanted to make a project out of doing more substantial test cartridges, as a sort of stress test for the protocol design before we committed to it. I got sidetracked trying to port EasyProg to MEGA65, then I got distracted with other things. (It's possible to just use EasyProg from the C64 core for making MEGA65 test carts, I just thought it would be cool. :) I didn't get far.) I also wanted to stress test my proposed extension to the CRT file format, as a potential input for EasyProg, Xemu, and maybe someday a CRT file loader built into the Freezer. I wrote the CRT file spec extension into the proposal on the wiki but I don't really have a way to verify it without building out more of the tool chain.

https://mega65.atlassian.net/wiki/spaces/MEGA65/pages/36962324/MEGA65+Style+Cartridge+Work+in+Progress

I'll close this issue. We can open new ones for any other ROM changes that might be needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants