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:
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
ICRregister at address
$DC0Dif, and only if, the sound goes high (
5Vprobably) 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
3of 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
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
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.