TransWikia.com

C64 cartridge emulation with ATmega

Retrocomputing Asked by MastErAldo on December 25, 2021

As a personal project I had the idea to create a custom cartridge for my Commodore 64 and use an ATmega 1284p microcontroller to emulate eproms and/or custom chips.

Basically my idea is similar to the one presented in this project from Hackaday:
https://hackaday.io/project/28120-c64-production-cartridge except that that project uses a PIC.

Now, my question is about how to wire some of the lines coming out from the commodore expansion port to my ATmega. As shown in the schematics below, address and data lines take 24 pins (3 ports in my mcu) leaving me with 8 pins available.
enter image description here
I did some research on the various lines provided by the expansion port, I found most of the information here: https://www.c64-wiki.com/wiki/Expansion_Port and here: https://www.c64-wiki.com/wiki/Bank_Switching

I’m pretty sure that to have a cartrige that can also be used as a custom chip other than an eprom I need the following lines connected to my MCU:

1. _GAME (I could use a jumper for this, but I’d prefer the MCU to control it)

2. _EXROM (same as _GAME)

3. R/_W (so I can also write to my MCU and not just read)

4. PHI2 (might be useful to synchronize with the system clock)

This way I have used 4 out of 8 available pins, but now I’m a bit confused about these remaining lines:

1. _IO1 and _IO2: “Signal is low if address bus is within $DE00-$DEFF”. Since these lines notify that some addresses are used for memory mapped IO, does it mean that they go low if address is within $DE00-$DEFF AND addresses $D000-$DFFF are bank switched to IO (Mode7 for example) or _IO1 and _IO2 lines go low just when the address is in the specified range? Because if it’s the latter, these 2 lines are useless as I can simply read the address lines and know if I’m in the $DE00-$DEFF range. Reading from the bank switching wiki, it also says: “$DE00-$DFFF is special and reserved for I/O with chips on the expansion port (by wiring the IOx lines of the port to the enable pins of the chips)”. Does it mean that those 512 addresses are always for expansion port IO regardless of the selected bank switching mode?

2. _ROML and _ROMH: these lines contribute to bank switching from the CPU side. Again I’m not sure I need those: from the description it seems I can deduce their state from _GAME, _EXROM and the address lines. Are these lines there just to simplify cartrige hardware (the same deduction i can make via software would require a series of logic ports on the cartrige) or am I missing something?

3. _DMA: this confuses me a bit: the explanation says: “if _DMA=Low the CPU can be requested to release the bus. It will stop after the next read cycle and all bus lines will go to high resistance state. So other units can use the computer hardware. At _DMA=High the CPU continues to work.” Does it mean that if _DMA is pulled low the cartrige can than access hardware like the c64 ram and write to it? Because if it’s like that I would expect address lines to be IO and not just Out like explained in the documentation above.

4. BA: More just for curiosity, since I don’t plan to use it, but: it’s marked as Input, but it also says that it’s controlled by the VIC chip. So, since it’s an input I would expect that the cartridge can control it, but without knowing what VIC is doing I suppose it could create some conflicts modifying its state.

So long story short: I’d like to know if my suppositions are correct or if I misinterpreted something before wiring the remaining lines, so I can know how to best use the 4 remaining pins on my MCU.

Thanks in advance.

5 Answers

More a side-note than an answer: There is the impressive Kung Fu Flash project which is doing exactly this and much more using a STM32F4 controller running at 168 MHz. It is all open source, so it should yield all the necessary information if someone is going to try something similar using a different controller.

Answered by cg. on December 25, 2021

This is intended to supplement the other answers here; you should also read them if you've not done so already.

Address Mapping

There are basically two possible situations when it comes to address mapping and interaction with the cartridge.

If you assert G̅A̅M̅E̅ and do not assert E̅X̅R̅O̅M̅, there's only one address mapping configuration possible because bits 0-2 of the processor's PIO output ($0001) are ignored by the PLA. This gives you:

  • RAM at $0000-$0FFF; this can never be disabled in any mode.
  • I/O at $D000-$DFFF. The C64 internal I/O devices respond to requests in $D000-$DDFF, but $DE00-$DFFF are free for your use. The I̅O̅1 and I̅O̅2 lines will be asserted for accesses to the lower and upper half of these areas, but you don't really need these because you already know that there are no C64 devices there.
  • Everything else "unmapped" as far as internal devices go. For accesses to locations $8000-$9FFF and $E000-$FFFF the R̅O̅M̅L̅ and R̅O̅M̅H̅ signals will be asserted, but in this mode you don't need them since you know that no C64 internal devices will ever be using this range anyway.

In this mode you can never get access to the C64 KERNAL, BASIC or character set ROMs. Writes will go through to C64 RAM, but you can never read from it.¹

In all other states that can be set by the cartridge (i.e., any where G̅A̅M̅E̅ is not asserted), the memory mapping is affected by the low three bits of $0001 as read by the PLA. The cartridge can't read these directly, since these are lines between the processor and PLA only, the only knowledge the cartridge hardware can have of what parts of the memory map won't be responded to by internal devices (causing great trouble if the cartrdige also tries to respond to a read) are the I̅O̅1, I̅O̅2, R̅O̅M̅L̅ and R̅O̅M̅H̅ lines. If you're using only software entirely under your control and understood by you, it can set the memory map appropriately to let the cart use other areas, but if any other software might change this map without disabling appropriate cartridge hardware, you are likely to see problems.

BA

I've checked the schematics, and from my limited knowledge it looks to me that the BA signal is an output from the VIC. But it would be good if someone with more knowledge than me would confirm this.


¹Actually, there are some special tricks that might let you get access to some of this ROM and RAM, but that's a subject for [other questions]rc 12372.

Answered by cjs on December 25, 2021

This is basically a duplicate for NES cartridge ROM emulation with Arduino or Pi?. The fact that the C64 is a bit slower doesn't change it in any way, so you might want take a look at the answers - as well as Martin Rosenaus's fitting answer.

To pull something like this off, you need a MCU about 15-25 times faster than an ATmega (using assembly, make that 50 times for C). With some external hardware this might be lowered, but not by much.

Now, if you're serious about the idea, you should take a look at Cypress' PSoC5 family. They not only offer a rather fast ARM core, but an incredible versatile programmable I/O section with several units that can be made into status machines, handling most signaling and data transfer on their own. Not to mention that building such a parallel system is much more fun in itself :))

Answered by Raffzahn on December 25, 2021

As a personal project I had the idea to create a custom cartridge for my Commodore 64 and use an ATmega 1284p microcontroller to emulate eproms and/or custom chips.

I doubt that this will work!

The reason is simply the time needed by the microcontroller to react on a signal change:

As far as I know, you have about 0.25µs to react on some edge on the C64 bus. (Some diagrams I found in the internet even mention much shorter times!)

Even if your microcontroller is running with 48 MHz, you can do 12 machine instructions in this time. Even if you use hand-optimized assembler code, this is not very much.

You'll have about 5 instructions to check if the C64 bus lines are in the correct state and in the case of a read from the module, you need at least 2 instructions to drive the data bus.

About 5 instructions are remaining for doing something. This might even not be sufficient to read a byte from the ROM!

(And if the CPU is too slow for hand-optimized assembler code, there is of course no chance to use code written in C or C++!)

Using 20 MHz clock frequency (ATmega 1284p) you would have only 5 machine instructions. Even when using hand-optimized assembler code, this is not even sufficient to check which address is accessed by the C64!

Answered by Martin Rosenau on December 25, 2021

Initial assumption you are making about the signals /GAME, /EXROM, R/W, and the clock are reasonable. There are some nuances with the other signals that you might need to consider.

The C64 has the ability to map the 4K of addess space at D000-DFFF as either system RAM or I/O. The only way your cartridge can know which one is mapped is using the /IO1 and /IO2 lines. If those lines are inactive, then its system RAM being accessed.

/ROML and /ROMH are, in fact, necessary to simplify cartridge design. A legacy cartridge could be just ROM chip(s) with no address decoding logic. So, it would rely on these signals to let the C64 do the decoding needed for Chip Select (CS). Of course, your cartridge controlling /GAME and /EXROM outputs causes changes in how these two inputs appear. It may be a simpler and more reliable approach to use these signals as they were intended.

/DMA and BA need to be used in tandem, but only if you intend to actually implement a DMA capability. This is true DMA, whereby your cartridge can be the bus master and can modify the C64's system RAM directly, bypassing the 6510 CPU. This could allow very capable, fast I/O, but seems out-of-scope for what you are trying to accomplish.

Answered by Brian H on December 25, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP