Commodore IR64

By Puck Meerburg

So, you’ve got a Commodore 64, a datasette adapter, and you want to run an IRC client-like thing on it. So, what do you do? Write a piece of code in C to use the adapter to communicate data over an extremely noisy connection two ways of course! (At least, that’s what I did :P)

The project is mostly based around a C code base, using cc65, as I am not really well-versed in 6502 assembly, especially when compared to my Z80 skills (hmm, reminds me, I should do the same with my ZX Spectrum, but that one is broken and doesn’t want to save/load via the connectors…)

The code is written mostly asynchronously, but with a lot of hacks as I am not going to try to set up an interrupt handler. Everything in here uses the CIA1 time of day 1/10 second timer…

There’s a few functions I use to run the whole screen:

void send_data(const char *);  // sets up internal state to send the data
void write_line(const char *); // Writes a line to the on-screen 22-line buffer

// Main loop parts:
void send_datasette();   // Runs a routine to write state to the datasette
void handle_datasette(); // Checks if data has been received, and stores it
void poll_input();       // Check for new input from the keyboard to send back

This all is just used in a simple while(true) loop, nothing special there.

But wait, there’s more!

While writing this program (I still am, can’t get the communication quite right yet, and not having any speedy transfer doesn’t help…) I have made a few notes:

  • Audio -> C64 is very simple, however: it is negative edge triggered, which means it will raise an interrupt and set bit 5 of the CIA ICR register at address $DC0D if, and only if, the sound goes high (5V probably) to low (0V). You could probably hide a very simple program in a song using a special fastloader, even…

  • C64 -> Audio is (as far as I have researched) simple too, but here you can set the signal to high or low - this is done with bit 3 of Zeropage address $0001 (TODO: does code in the C64 KERNAL set it low again?).

  • My graphics card is very very noisy - while recording the C64 datasette using audacity, I noted a lot of regular noise, which seemed to peak when audacity moved the visible part of the audio… That’s what I get for using a graphics card from 2006, I guess :)

My current data transfer algorithm

My current ‘algorithm’ I use for transfering data is even more stupid: if a trigger (so from 5V to 0V) happens within about 0.2 seconds (based on the CIA1 TOD 1/10 second timer!) it is a 1, else it is a 0. This does make it easy to synchronize it, presuming the transfer isn’t stopped in the middle of a byte - just trigger it 8 times slowly, then it will read $00.

This algorithm, as expected, is very suboptimal and I am looking for better algorithms to implement, however I think I may need to do some handshaking, e.g.:

C64   |    PC
 <-- trigger   The PC triggers the datasette input,
                   indicating it wants to send data
  ---0V-->     The C64 acknowledges this by pulling
                   the output low
 <-- data      The data is sent as a simple serial
                   bitstream, in a PWM-like format
               The C64 reads this bitstream, parses it,
			       and checks the checksum at the end
  ---5V-->     The transfer succeeded, so the output
                   goes high again

This should be a lot faster, but I have to write some 6502 assembly for this probably, and I will lose the asynchronous part, but I think it will be OK to have that, as long as it can send more than 10 bps (best case) or 4 bps (worst case)!

I have put off the transfer part because of the above mentioned slowness, but also because current C64 emulators (tested with vice) don’t support my current behavior, e.g. complete abuse of the datasette system. So yeah, that should be fixed some time.