6

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
8

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
11

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!

7

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

0

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.

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.