4

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.

  • 1
    If I wanted to use PHI2, I would use a clock multiplier chip and skip the external crystal for it. The PT7C4511 has a minimum input frequency of 1MHz so this should work. Unfortunately, it's maximum factor is 8, so you need two of them in series (configured x5 and x4). But they are cheap, e.g. mouser wants €10 for 10 pieces. If I wanted to do DMA, I wouldn't try an asynchronous design. – Janka Apr 26 at 21:30
  • That's indeed a good idea, not being an electronic expert i thought i could let the MCU run at 20mhz with an external clock and then just busy-wait for PHI2. It's not efficient, but it should still be fast enough to feed the data bus in time. – MastErAldo Apr 26 at 21:49
  • Well, synchronous code is simpler and better to debug. That's the trade-off. – Janka Apr 26 at 21:55
6

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.

  • Thanks, now I have a clearer idea, so, do you confirm my thought that /IO1 and /IO2, in order to be pulled low, the CPU must access addresses in the range $DE00-$DFFF AND address range $D000-$DFFF must be set to I/O (so that, for example, a write to $DE00 in mode 1 that maps that range to RAM won't alter /IO1) ? Also, with DMA activated, address lines must act as input? otherwise I couldn't imagine how to write directly to C64 ram – MastErAldo Apr 26 at 20:24
  • Correct. Otherwise expansion port I/O devices would cause system RAM D000-DFFF to be unusable. For DMA to work, the busses are bi-directional. Expansion cartridge can read or write system RAM. The CPU just sits there with busses in high-impedance state until DMA is done. – Brian H Apr 26 at 20:53
  • _ROML and _ROMH are for ROM areas what _IO1 and _IO2 are for the IO area. You have to use those instead of decoding the upper address bits yourself to make bank switching work. – Janka Apr 26 at 20:53
  • Then I have just one last perplexity: If_GAME and_EXROM are high (no cartrige inserted) and then I change the data/address lines, would this mess with what the C64 is doing or,since_GAME and_EXROM are high,everything on data/address lines coming from the expansion port is ignored? If it's the first one,then you are right,I also need to listen to_ROML and_ROMH to avoid writing on the data bus when in reality the cpu is speaking with RAM and not with the MCU,but if the lines are ignored, my MCU could always write since if the address is not for it,data is simply ignored. Hope I made myself clear – MastErAldo Apr 26 at 22:29
  • The expansion port isn't isolated from the internal bus. You have to put D0-D7 into input/High-Z state unless your device is addressed with ROML/ROMH/IO1/IO2. A0-A15 must be inputs/High-Z unless you meet all the criteria for entering a DMA cycle. – Janka Apr 27 at 1:12
7

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!

5

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.