Subscribe via RSS

Commodore 64: Fixing RS-232 Serial Limitations

This is going to be a bit of a rant, so I apologise in advance. I have just spent a good 75 hours getting the C64 to talk to an Arduino via RS-232 and each step of the way has been painful. Initially, communications were sorted and data made it across to the Arduino. All wasn't as it should be as I realised that the character mappings (PETSCII vs ASCII) meant that the data had to be translated. Once that was sorted, it was a matter of sending data back. Again, character mappings were required. Past this, I then wanted to send 64-bits of raw data. Not characters, but 8-bits-to-a-byte raw data as I wanted up to 64 sensors and therefore only 8 bytes to transmit this. Turns out that the C64 is hard-coded to transmute the serial value zero to something else...

Diagnosing the issue

I'd already built up a large-ish application on the C64 for controlling the trains. There was also a large amount of code on the Arduino side. Due to this, any support libraries or includes or other interrupt-hacking routines could've been interjecting and mangling data. After a lot of to-ing and fro-ing, compiling on windows and switching to SD card... I got jack of the speed of which I was able to debug on real hardware.

Fortunately, VICE came to my rescue. Not only does it have a debugger, but it also emulates the RS-232 User Port Serial and shit... it even reproduced the error! Let me show you how to set that up...

Configuring VICE's User Port Serial

rs232-userport-settingsOpen VICE and choose Settings -> Catridge/IO Settings -> RS232 userport settings.... Enable the RS232 userport emulation, leave it as device 1 and set the baud rate to 2400. Hit OK.

rs232-settingsBack to Settings -> RS232 settings.... We want to edit RS232 device 1. Fill in the text box with the relevant IP and port of the machine you wish to communicate with. If you're going to use my TCP server below, then enter 127.0.0.1:25232.

Close VICE. Next time you open it, VICE will attempt to connect to a TCP Server listening on your localhost IP on port 25232. You can configure this to whatever you want, but we are going to use the default. VICE will then treat the connection as RS-232 and provide any data received to the internal C64 user port. It will also send out any data sent to the user port via this channel.

Now that we're done with the settings, we need to give it something to connect to.

TCP Server in C#

Download the code here. I've rigged up a very simple C# TCP server for VICE to connect to. The TCP server must always be loaded first, otherwise VICE will continue silently and never send out any data. The code is also overly-primitive and, upon closing VICE, the TCP server will need to be restarted prior to starting another instance of VICE.

simple-tcp-console

Once running, you can use the 1-8 keys to set the bits of the byte to be sent. Alternatively, pressing any keys of the alphabet will set the byte to that letter in ASCII.

Finally, hitting Enter will transmit the byte.

C64 RS-232 Serial Test Program

Grab the Serial Test Application here that I wrote to test the RS-232 serial port on the C64 using Johan's driver. There's a batch file in there that you can use to run the program. Just make sure it knows where VICE is.

Make sure the TCP Server is loaded first, then run the batch program. It's set to auto-start the binary. If this doesn't work, then you can open VICE and via Settings -> Peripheral Settings... configure the directory where the binary is. You can then LOAD it as per usual.

peripheral-settings

Once loaded, you'll be in the app. It's pretty straightforward and will just print out the data that has been received.

c64-serial-app

Make sure that the TCP Server has shown that the connection has been established. You can now press the keys as per the instructions above on the TCP Server and send data. At the bottom is a timer and a number of bytes received. As each byte comes in, it'll be displayed up the top. It'll also be numerically represented down below, next to in:, above the clock.

At this point, type random letters from the alphabet and send them to the C64. You'll note that lowercase get translated to uppercase thanks to the ASCII to PETSCII translation. Tinker with the 1-8 keys to set the relevant bits in the byte to send and then hit enter. Watch that 1 = 1, 2 = 2, etc... until you try to send a raw zero... bugger.

Debugging the problem

Ok, we've now worked out that, even in the land of emulation, sending a zero to the RS-232 serial port on the C64 produces an 0x0d. This rules out the 'client'... both the Arduino and my TCP Server send raw zeroes and both VICE and a real C64 render the wrong character once received. From here, it could be the custom driver, the cc65 support libraries or something much more evil.

Debugging on real hardware is something I'm scared of... I'm so used to multiple windows and multi-tasking that I'd like to do it in a more comfortable environment. Thankfully VICE has a monitor that can help us. Open this up via the File menu and prepare to delve into the land of assembler! Note that, when open, the C64 emulation is paused.

vice-monitor

Once the emulator and application are loaded, choose View -> Disassembly Window. You'll get a little window showing the entire memory of the emulated C64, disassembled. This means that VICE, with it's knowledge of C64 6502 CPU op-codes, has translated the raw memory into known commands. Fortunately, it also has information on the mapping of the memory and therefore does a very good job of this translation. Unfortunately, there is no search command... so we will need to scour over this to find what we are looking for.

What are we looking for?

The million-dollar question. The serial test app that we've compiled for the C64 (and executed in VICE) is based on the sample code by Johan. It also includes his 2400-baud serial driver. Looking at his source code, we can see the routines that are called when we call the ser_get function from our main loop. If you browse over to his source, you'll find the relevant lines in driver/c64-up2400.s.

GET:   
        ldx #2
        jsr CHKIN
        jsr rshavedata
        beq GET_NO_DATA
        jsr BASIN
        ldx #$00
        sta (ptr1,x)
        jsr CLRCH 
        lda #<SER_ERR_OK
        tax 
        rts

vice-disassemblyThe snippet above was copied on the 24th of June, 2016. This code has hopefully been updated by now, but what you see above is the function, as it was back at that time, which reads data in from the RS-232 Serial port. I can't fully explain each line, but what we can do is find this chunk of code in the disassembly window. Unfortunately, we'll be doing this by hand as I don't know a better method to search for it.

After a lot of scrolling... this code has been found in memory location $238A. ldx #2 is seen as LDX #$02. The further jumps are visible, but their happy names are gone. Instead we see the memory addresses of each of those functions. You can see that I've also clicked the row in question. This sets a breakpoint, represented as a red highlight on the row.

Triggering the debugger

From here, we can actually close the debugger. Make sure that the red row is set prior to closing the main Monitor window. If you just did this, then you'll find that the debugger window popped back up straight away and now our red breakpoint is a lovely shade of teal. Teal? What happened to Red? In the disassembly list, a blue line indicates the location of the next line to be executed. Ours is teal because it's a set breakpoint AND it's the next line to execute. If you're bored, click the line and it'll turn blue. Click it again and it should return to teal.

Ok, we've triggered it... but why? No data was sent down the channel! It turns out that, if you look at the source above, there's a hint on the line with GET_NO_DATA. The ser_get is called from our code on each program loop; it returns a SER_ERR_NO_DATA when there is no data... so this line we've broken on is too early in the driver code. We really want to set a breakpoint at the first line executed when there is data.

So step through the code. To do this, press the 8th button on the toolbar in the Monitor window. Press it a few more times and watch the execution. We're currently 'stepping over' functions; the two JSRs might well send the program counter off into other parts of memory and execute code, but we don't care. Once you're on the BEQ GET_NO_DATA line, press it again. Note that we now jump down to the location of GET_NO_DATA and exit out. We can assume that this jump is the significant point where, when data exists, the execution continues straight through and does not jump.

debugger-at-char-received

Now that we know where this junction is, we want to clear our previous breakpoint and set one on the line directly after BEQ. Set the breakpoint and close the Monitor. It should not pop back up this time.

Viewing the data coming in via Serial

Ok, we've set our breakpoint in the driver and it hasn't triggered yet; let's hope it does when data is received. To test this theory, we'll need to use the TCP Server from above. If it's not already running, close VICE first and then start the Server. You need to have the TCP Server running prior to VICE for the connection to be correctly established. Once everything is running, ensure that the server has reported that the connection is established. Confirm your breakpoint is still in place; VICE doesn't save these, so you'll need to go hunting again if you lost the position. Once everything is configured, make sure you close the Monitor window to allow the C64 emulation to proceed. When the Monitor window is open, the CPU emulation is halted!

Once ready, focus on the TCP Server window and press the a key. Make sure it reports back that the byte is set to 97, which is ASCII. Press the enter key and you should trigger the breakpoint in VICE.

Ok, we've got it, our breakpoint was hit and we presumably have data somewhere? Our breakpoint is actually on a JSR through to the address $FFCF. At this point in time, this address is unfamiliar. All our previous driver code has been around the $2300 mark. Due to this, we're going to step over it. The next call is an LDA which uses a pointer + offset to load a value into register A. Stepping over this, we see the following in the Monitor window:

(C:$2394) n
.C:2397  A2 00       LDX #$00       - A:61 X:03 Y:00 SP:f2 ..-.....    6728381

Interesting... A:61 hey? That's the ASCII HEX value for a lower-case a! So there it is, the value has come through and we can see it in memory. You can close the debugger now and play around with data on the TCP server to see how it arrives on the C64.

Note that as soon as you try to send a zero from the TCP Server, it'll appear as an 0x0d. At this point, we've honed in on the location where the char is read in, but we don't know what code is hosted up in the $FF00 range?

C64 Memory Mapping

Time to go deeper. Thank god we're working on a seriously old system that has been thoroughly documented. You'll find the memory map of the Commodore 64 here. If you scroll down the page, you'll find that the area in question contains the Kernal (Kernel?). At this point... we might as well retire.

Retire? Yes. The KERNAL is ROM, you know... READ-ONLY MEMORY. It's the raw system code burnt onto chips on the motherboard and it is a fixed entity. Those bytes can't be hacked... anything we try to do will require reproduction of kernal commands in the 'user' area. Should we try this? Sure... but first it'd be nice to know what's going on.

C64 Kernal Disassembly

Again, thanks to the age of this system, there's a full disassembly of the C64 Kernal here. We know that we're jumping in to location $FFCF, so browse down to that area.

FFCF   6C 24 03   JMP ($0324)   ; (F157) input char on current device

Oh look, it's just another JMP. Fortunately it's commented and we know to browse through to $F157. Now we are in murky waters... not many comments here. We get a heading of ; input a character, but then not much else. The assembly, to the naked eye, looks like a switch statement. It seems to be going through checking which device to read from. In our case, we can actually step through it in the debugger. If you step into the JSR #$FFCF then you'll be able to watch it jump as you send in characters from the TCP Server.

The basic trail jumps through: $FFCF -> $F157 -> $F166 -> $F173 -> $F1B8 (a.k.a. read a byte from RS-232 bus) via F177. At $F1BD, there's a comparison of the incoming byte to 0x00. If there's no match, then the code jumps out to $F1B3 and returns the value. If there is a zero, then the following occurs:

F1C1   AD 97 02   LDA $0297
F1C4   29 60      AND #$60
F1C6   D0 E9      BNE $F1B1

If the AND fails to compare, then the jump happens at $F1C6 to $F1B1. Looking at $F1B1 made me cry. The code implicitly inserts an 0x0d, overwriting the byte that was read into the buffer and then returns it.

I don't quite know what the LDA from $0297 is and why it is AND'd with #$60. I'm sure there's some RFC or some prehistoric rule back in the late 1980s that said if a modem or other serial device returned a zero, then it actually meant it to be a carriage return. Maybe it was a BBS thing? I'll continue digging and attempt to comment this area of code, but for now... we know that it's futile. The KERNAL ROM is fighting us and thinks it knows better!

A valid workaround

Righto... what do we do here? I initially thought it was a complete loss and gave up. Further Sapporo made me realise that this call was made from our custom driver. What if we specifically mention that our custom driver is built to handle 'zero' byte data and implement a work-around? If we copy out the code from the Kernal and re-produce it in the driver, then we can effectively resolve this (I wont say bug, I'm sure they had their reasons!) issue.

So, the trick here is to grab the portion of code from the full disassembly of the C64 Kernal and build it into a function in Johan's driver.

We know the entry point is $FFCF. This is a JMP to the switch statement which chooses the device. We know that this is the RS-232 driver, so we can skip that part and copy the code from $F1B8 to $F1C8. I've pasted this in below.

; read a byte from RS-232 bus

F1B8   20 4E F1   JSR $F14E
F1BB   B0 F7      BCS $F1B4
F1BD   C9 00      CMP #$00
F1BF   D0 F2      BNE $F1B3
F1C1   AD 97 02   LDA $0297
F1C4   29 60      AND #$60
F1C6   D0 E9      BNE $F1B1
F1C8   F0 EE      BEQ $F1B8

That CMP #$00 is the pain. Let's just jump to $F1B3 all the time. Actually... $F1B3 is just CLC and RTS. Let's just write that. We also can't directly BCS to $F1B4, so we'll need to JSR to a closer function and then call JMP. If we JMP directly then we'll lose our position in the stack.

F1B8   20 4E F1   JSR $F14E
F1BB   B0 F7      BCS $F1B4   ;re-write this to a BCS and JMP
...    ...        CLC
...    ...        RTS

With my patch above, I've removed (what seems to be) a re-try loop in the code. If it falls all the way through to $F1C8 then it returns to $F1B8 and tries to read a character again. I haven't seen this state occur in real life, but I'll keep an eye out and try and work out when this actually occurs. It seems that the AND #$60 must check for an error state which I'm yet to encounter.

I don't actually know the assembled opcodes off-hand. We will write this as standard assembly into the c64-up2400.S driver source file and then it'll write the opcodes on compilation. So, from line 139 we slap in:

;----------------------------------------------------------------------------
; OTHERFUNC: Shortcut to Kernal code
;

OTHERFUNC:
		jmp $F1B4
		
;----------------------------------------------------------------------------
; OURBASIN: This is a minimised call to get the character from the buffer.
; The Kernal code does not allow zero bytes (0x00)... this does.
;

OURBASIN:
		jsr $F14E
		bcs OTHERFUNC
		clc
		rts
		
;----------------------------------------------------------------------------
; GET: Will fetch a character from the receive buffer and store it into the
; variable pointer to by ptr1. If no data is available, SER_ERR_NO_DATA is
; return.
;

GET:   
        ldx #2
        jsr CHKIN
        jsr rshavedata
        beq GET_NO_DATA
        jsr OURBASIN
        ldx #$00
        sta (ptr1,x)
        jsr CLRCH 
        lda #<SER_ERR_OK
        tax 
        rts

I'll give the kernal function a real name soon. Right now the basic point is that we write our own BASIN function that is just a tiny subset of the greater procedure and then skip the part where it inserts that shitty little 0x0d.

it-worked

Either way... compiling this (see notes on that here) saw the bloody thing work! I'm going to get in touch with Johan now and determine what needs to be tidied up to get this trick included in the trunk.

That was fun!

22Jun/160

Serial Train Controller (C64+Arduino)

I'd previously attempted to use the parallel port for train control but have now switched to the Serial port for communications. I've also slapped an Arduino in between the layout and the controlling (Terminal) device. The Arduino will be programmed to understand data coming in from the Serial port, which can be fed from any device which is capable of outputting an RS-232 signal. For the example below, I'll be doing this from a Commodore 64. See this article to learn how to build a Serial Port for your C64.

Designing a communication language

Since we'll be using the RS-232 standard, we're able to define a language that any Terminal can speak to control the Arduino. This will be based on ASCII for command verbs and then raw byte data where targets and values are required. For instance, we could set throttle number 1 to 150 or point number 4 to left. Without being too tricky, 3 bytes is adequate to communicate these commands: C|T|V. If we wanted the data messages to be 'human readable' (so you can print them straight out to a file or other serial line), then we should send through 7 bytes C|TTT|VVV, where the target and value are padded out with zeroes. Fortunately, we only need these two to be 8-bit values, which is the definition of a byte. We'll simply have to convert them to ASCII if we ever want to print or display them.

Next, we'll need a command separator. I've chosen the exclamation mark '!'. Hence, when sending commands, our messages will be 4 bytes long. The recipients will need to wait data, ensure there is at least a full 4-byte message with a terminator and then start processing.

Important Note: As is described down below, I found out the hard way that not all text is equal. I've naively mentioned 'ASCII' two paragraphs above and jumped straight onto the C64, compiling strings and sending them down the wire. For some reason, although I full-well knew that I was coding in PETSCII, I neglected to think that it would actually send it down the line! Long-story-short, when the C64 sends a capital A, it actually sends a character that does not map to ASCII. Also, when it sends a lower-case A, it is actually seen in ASCII as an upper-case A!

Moral of the story? Make sure you understand what character set each device is transmitting and receiving!

Talking back to the Terminal

Although everything written above indicates that most of the communication will be one-directional; this won't always be the case! Some of the commands in the table below will actually be asking for data from the layout. This will be in the form of sensor blocks, where optical or occupation sensors will exist on the layout and be wired to binary input data. The Arduino will be hooked up to shift registers which, when daisy-chained, will be capable of 'watching' up to 64 inputs.

To get this data back to the terminal, we'll send through a message of 10 bytes. This will start with the letter S and be followed by 8 bytes of binary data indicating the state of all connected sensors. Finally a '\0' will be appended to indicate the end of the message. The Arduino wont expect a response to this; if the Terminal has failed to receive, then it can simply request the data again.

The Command Table

Now that we have our expectation of 3 data bytes and one terminator per message, we can start to define all of the commands expect to send.

Command Target Data
T

Set Throttle

1 or 2

The Arduino will have two separate PWM throttles.

0 to 255

The throttles will supply 12v DC PWM. The wave-cycle will be dependent on the value from 0-255. This maps to 0-12v. Hence 128 should be roughly ~6v.

D

Set Direction

1 or 2

Each throttle has it's own direction.

F or R

Forward or Reverse.

P

Toggle Point

1 to 8

8 separate points will be connected.

L or R

We'll use ASCII here to make life easier. Left or Right can be specified.

S

Read Sensors

0

We don't need to specify a block... we'll get the whole 8 bytes back regardless.

0

No value required here. We'll send a zero for padding.

Code for the Arduino

The Arduino will need to keep an eye on the serial port and act on commands when they appear on the channel. Data from the serial port will come in as singular bytes, so they'll need to be written into a buffer and processed once a whole message is found. In case the Arduino struggles, it'll need to be able to understand what a whole message is and discard anything that looks incomplete.

I'll skip the bits on PWM throttles, reading sensors and LED Matrices here... I'll describe all that in another article. Currently you can find individual articles on each of these topics on this site if you need the information straight away.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(8,9); // RX, TX

void setup() {
	// set the data rate for the SoftwareSerial port
	mySerial.begin(2400);
}

...

void processCommand() {
	int x = 0;
	switch (serial_buff[0])	{
		case 'T':
			setThrottle(serial_buff[1], serial_buff[2]);
			break;
		case 'D':
			changeDir(serial_buff[1], serial_buff[2]);
			break;
		case 'P':
			//adjust point
			break;	
		case 'S':
			//read sensors and return the data.
			break;
						
	}
	
	for (int x = 0; x < SERIAL_MAX; x++) serial_buff[x] = '\0';
	last_pos = 0;
}

...

void loop() {
	//if there's data and we've not read the end of the current message.
	if (mySerial.available() && (last_pos == 0 || last_char != '!')) {
		last_char = mySerial.read();
		serial_buff[last_pos] = last_char;
		last_pos++;
		if (last_pos > SERIAL_MAX) {
			//then we need to do something drastic
		}
		if (last_char == '!') processCommand();
	}
}

The snippet above checks if there's data and if we don't already have data. If the buffer is empty, then it'll read a character into it. If that character happens to be '\0' then it'll prevent further reading and process the message.

Controlling with a Commodore 64

cc65 has all the libraries we need to get the serial interface up and running; see more on that here. We'll use a text-based interface and control everything with the keyboard. At a later date I'll write a GEOS-based GUI.

I attempted to use the Tiny Graphics Interface libraries that cc65 provides. Unfortunately, that would've also meant writing a text renderer or graphical font library as the basic 'text out' for TGI isn't implemented on the C64. Staying with text-mode was to be easier and PETSCII has enough cool symbols to draw throttles and the like.

c64-trainctl2

The screen displays the throttle, just one for now, and the direction. It also provides a clock and a schedule. The user can add and edit items in the schedule and, when in run mode, these will be executed accordingly.

void sendCommand(unsigned char c, unsigned char t, unsigned char v) {
	ser_put(c);
	ser_put(t);
	ser_put(v);
	ser_put(33);
}

...

void main() {
	...
	sendCommand('d', 1, current_direction);
	sendCommand('t', 1, current_throttle);
	...
}

I've left out most of the guff ... I'll include the full source soon. For the meantime, you'll see that I've put individual characters to the serial channel for reading at the Ardunio end. Specifically they are lower-case! You'll also note that I write 33 instead of the literal character !. The reason is, of course, that the exclamation mark in PETSCII is not the same as ASCII.

What's Next?

This works. The train happily moves back and forth using the signals sent from the C64! It's overly boring though and based on the clock. I want sensors read back to be able to trigger events... so I'll hook up the multiplexing and post again shortly.

19Jun/160

Commodore 64: Serial Interface to Arduino

So, in my previous post, I was heading towards building an archaic circuit to control trains with the User Port. This would've worked, had I spent a lot more time and built a very complex circuit. Instead I've now chosen a new path... let's hook the C64 up to an Arduino and do most of the work there. The C64 can be the display and input/output for controlling the trains.

Interfacing both components

The C64 User Port has both a 'parallel port' with 8 i/o pins and a serial port. I initially wanted to use the parallel pins, but came to the conclusion that I'd have to write my own language on both sides and deal with the data transfer timings and clock synchronisation. Instead, it'll be easier to use industry-standard RS-232!

I suppose this is a bit of a cop-out. I wanted to build something that was dated to the level of technology that existed back when these machines were in their prime... unfortunately my electronic knowledge isn't up to scratch... so getting to a variable 12v output wasn't overly easy. It also would not have been PWM. Due to all this, including the Arduino into the mix isn't such a bad idea. Plus, everyone I'd asked for help told me to do this... even sending me links to my own blog posts :)

DTE vs. DCE

Serial plugs have a single channel, with each end having one transmit (TX) and one receive (RX) pin. Each end will send data down the cable via the TX pin and expect to receive data on the RX pin. Standard serial cables are 'straight through', meaning that the TX pin is connected to the TX pin and likewise with RX. Doesn't quite make sense, does it? How are to separate devices meant to eachother if they are both transmitting down the same singular TX wire and hearing silence on the RX?

This all becomes clear once you realise that devices fit into two categories: DTE (data terminal equipment) and DCE (data circuit-terminating equipment, also known as data communication equipment.) In the end, these two devices boil down to one being the master (the DTE) and one being the slave (the DCE.)

Of course, you can purchase 'cross-over' cables for serial connections. These are known as null-modem cables and allow you to hook two DTEs together. Say, for example, you want to transfer a file from your PC to your Amiga, or somesuch!

In my previous serial project, when I connected the IBM receipt printer to the Arduino, I needed the Arduino to be the master, and so I hacked around until I had correctly configured it as a DTE. This time around we want the Arduino to be the DCE. Due to this, be careful to follow the pinouts and wiring from the serial port to the MAX232 in the circuits below!

Note: For further reading/wiring on RS-232, there's a good article at avr Programmers and another at Advantech.

C64 Serial Port

The User Port on the C64 also has serial connections. These are TTL and so need to be brought up to the RS-232 5v standard. The MAX232 IC will do this for us quite easily. We'll also use one at the other end for the Arduino.

C64-RS232

The circuit is derived from 8bitfiles.net. This circuit was also correct in that the pins are wired up as DTE. This means that you could use it, as-is, to also hook to a modem or any other DCE device.

The MAX232 needs few extra components. Fortunately, these extra components are identical on both ends, so buy everything in duplicate! The capacitors are all 1.0uf electrolytic. I used 1k resisters on the LEDs so as not to draw too much current from the User Port.

Arduino Serial Port

This is nearly the same circuit as the C64 end. The funniest thing happened here... if you google enough 'arduino max232' you'll just loop back around to my post, from ages ago on interfacing an IBM printer to the Arduino. Just make sure you don't use that circuit! It's DTE, we need DCE as per below! I've left out the RTS/CTS as I don't intend on using any form of handshaking in this project. It's still in the circuit above for the C64 so that you can use the port for other purposes.

ARDUINO-RS232

As per usual, make sure you DO NOT apply 5v in the wrong direction... I did and it ruined a few caps and possibly the IC. Garbage then came through the serial port. If this ever happens, then throw out the components and start again; you'll never be able to trust them.

Also make sure that you use the 5v pin on the Arduino. AREF is NOT a valid voltage source.

Hooking it all together

Build both circuits above and give one a male and the other a female db-9 connector. The DCE device usually gets the female, so put this on the Arduino-side!

DSC03947 DSC03956 DSC03954

DSC03955 DSC03950

If you want to roll your own cable, then grab some grey IDC and two crimp-style plugs. Just make sure that you have pin 1 matched to pin 1. If you're splitting the cable, then paint a wire (or use a marker) to ensure that you get the orientation correct. It's really easy to confuse pin 1.

DSC03998 DSC04000 DSC04002

From above, you can see the pin numbering. I slid the second port all the way to the end, prior to crimping, to ensure that the numbers matched up. Using the red '#1 wire' on the cable worked wonders too.

Testing with Strike Terminal 2013 Final

Download Strike Term 2013 Final from here and then get it to your C64. I copied the D64 to my SD2IEC and loaded it up. Hit M and select User port. Hit b and switch it to 1200 Baud (or other baud, depending on what you've configured in the Arduino.)

DSC03976 DSC03978 DSC03957

DSC03958 DSC03964 DSC03965

Once ready, hit f5 and then hit enter on the default server. This'll start sending modem AT commands down the serial and they should start showing up at the other end. Either open the Arduino Serial Monitor... or edit the code to display it. I bought some 8x8 LED Matrices to render the data coming in.

DSC03963 DSC03971

There were no real caveats here... everything just worked! Press f3 to get to the terminal. Hit commodore+e for local echo and then commodore+i to 'send id'. You should now be able to type freely... everything will be sent down the wire.

DSC03983 DSC03984 DSC03985

DSC03986 DSC03987 DSC03988

At that point I only had one matrix... so the last char typed was displayed.

Writing C code to use the Serial Port

Nanoflite has written a 2400 baud User Port Serial Driver for cc65. I originally tried to use this at 1200 baud, as that's what I'd been using everywhere and heard it was the max the User Port was capable of. It turns out that this driver only supports 2400 baud! Download it and put the source somewhere.

Switch to the driver directory and compile it:

cl65 -t c64 --module -o c64-up2400.ser c64-up2400.s

Copy this to the folder that has your source file it. I slightly modified the example.

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <serial.h>
#define DRIVERNAME  "c64-up2400.ser"

static const struct ser_params serialParams = {
    SER_BAUD_2400,      /* Baudrate */
    SER_BITS_8,         /* Number of data bits */
    SER_STOP_1,         /* Number of stop bits */
    SER_PAR_NONE,       /* Parity setting */
    SER_HS_NONE         /* Type of handshake to use */
};

int main (void)
{
  int xx;
  
  clrscr();
  puts("C64 serial ...");

  // Load driver
  ser_load_driver(DRIVERNAME);

  // Open port
  ser_open(&serialParams);

  // Enable serial
  ser_ioctl(1, NULL);

  for (xx = 0; xx < 10; xx++) {
	ser_put('C'); 
	ser_put('6'); 
	ser_put('4');
	ser_put('.');
	ser_put('.');
	ser_put('.');
  }
 
  return EXIT_SUCCESS;
}

Compile this:

cl65 -t c64 -O -o trainctl2 trainctl2.c

I then put it on the SD2IEC and loaded it via LOAD "0:TRAINCTL2",8 followed by a RUN.

DSC03993 DSC03989 DSC03991

DSC03992 DSC03994 DSC04003

Shit... worked... this is great! Next it's time to put a PWM throttle onto the Arduino and control it from the Commodore... I'll tinker with graphical programming in C also.

7Jun/160

Commodore 64: Datasette Maintenance

So, Double Dragon had issues loading on a Tape Drive I'd acquired. It had come in a mouldy box, so I had a hunch that the drive itself would need a thorough clean and alignment.

There are multiple alignment tools, downloadable as disk images or PRG files. I could copy these onto my SD2IEC, but there was an issue: the SD2IEC gained power from the Datasette port which was now in use by the Datasette drive itself!

Powering the SD2IEC from the Datasette Cable

Fortunately, the Datasette plug has a screw that makes accessing the internal pins very easy. I opened it up, taking note of the cracked, flimsy plastic, and inspected the contents.

DSC03854 DSC03855 DSC03856

The green wire, pin 2, is the +5v that we're after. Bare back some plastic on the wire so we can solder to it. I found a male+female standard round DC power socket to use. Make sure the female is on the 'power' side, otherwise you'll have a potential for shorts if there is exposed bare metal with current flowing through it. Of course, the outer metal is ground, but still better to be safer than sorry.

DSC03857 DSC03858 DSC03859

From here I soldered on the plug and grinded out some plastic from where the main cable feeds in. This allowed the plug to hang out the end. Not the cleanest job, but it worked quite well. I sorted wanted to feed it out the side where the ground wire is... but I hadn't left enough length.

DSC03860 DSC03861 DSC03862

Next, on the SD2IEC end, bare some wire also. Grab the plug and solder it on, then use some form of insulator to tidy it all up. Nothing a little duct-tape can't fix.

DSC03863 DSC03865 DSC03867

Everything was plugged in and good to go!

Cleaning the head

Everyone recommends alcohol (isopropyl) wipes for this. The wipes have the benefit of leaving little residue and drying cleanly. You'll find that KFC wipes are also usable.

DSC03876

Take the swab out of the packet and wipe the heads, specifically the central metal one. I didn't actually know which way to wipe, or how much pressure to apply. So just be gentle and attempt to remove any visible dirt. Don't put a tape back in until everything is try.

Aliging the head

There are a few options here. Download Cassette Azimuth, also known as 'Recorder Justage', and Minimal Head Align. Both do the same thing, the former is more complex.

I copied them both onto the SD2IEC and then loaded them via the file browser. Cassette Azimuth is easy to work with. Load it up and then hit play on your tape. If nothing is happening then you'll need to start adjusting the player already. If you see data, and it's erratic, then you'll also need to adjust. The goal is to have straight vertical lines.

To actually do the adjusting, there is a tiny screw-hole above the rewind button that a small jeweller's screw driver will fit through. When the tape drive is playing, the hole lines up with the head adjustment screw. Turn this screw all the way clockwise (not too much pressure!) and then turn back in small increments as required. Pause between turns to let the screen update with the new readings.

DSC03868 DSC03869 DSC03874

You can hit F1 to get the guide-lines for the data. I couldn't work out if the data lines were meant to draw over the top, or in between, or where... but at least I got them vertical!

DSC03875 DSC03877 DSC03878

Here's the same process with Minimal Head Align. The app is much more raw; it starts off with a screen full of garbage which starts refreshing once you start feeding it data from the tape drive.

DSC03880 DSC03881 DSC03888

Back to Double Dragon

I had assumed that all the loading issues were from a dirty/misaligned head... so I thought I'd try the game again now that the tape was producing cleaner, more vertical lines on the test programs. I didn't have much faith... but it worked! It took just as long as last time, but this time I got to the first level!

DSC03890 DSC03891 DSC03892

The graphics are intense... hah. Controlling the character was hard at the start... then I realised it was because my controller probably hadn't been used in a decade. I'd found a sega mastersystem controller at a second-hand shop over the weekend. Works perfectly.

DSC03900 DSC03901 DSC03905

So as the game was loading, it got to the point right before the title and asked me to reset the counter to 0. Turns out this is so that, when you die, you know where to rewind the tape to.

DSC03910 DSC03912 DSC03914

From there you rewind to zero, press play... wait for the load and then have another go. I might try source the game on floppies to see if the extra data capacity allowed for a different version. Hmm... then again if this review is anything to go by, then there's no hope.

27May/160

Commodore 64: cc65 and the User Port

Ok, we've build the user port to parallel port adapter. We've build the parallel port interface. We've hooked a shift register onto it and now we want to control it. Doing this through BASIC would be fun... but compiling real C code and getting this onto the C64 will be thoroughly more enjoyable. Go and download cc65, along with your favourite emulator. VICE works very well on Windows.

Getting a sample running

Before we even think of real hardware, let's get the samples running on VICE. Once you have everything installed, open a command prompt. Switch to the directory where cc65 is installed. Also set your path to the cc65/c64/bin folder. Run cc65 to check if you've got everything correct.

Microsoft Windows [Version 10.0.10586]
(c) 2015 Microsoft Corporation. All rights reserved.

C:\Users\windows_user_1>d:

D:\>cd cc65

D:\cc65>set path=d:\cc65\bin

D:\cc65>cc65
cc65: No input files

D:\cc65>

Download the samples from the github repository and save them nearby. Switch into the samples folder. Choose a sample to compile; I chose ascii. Compile it. Don't expect much from the compiler if there is no error. We're going to use cl65 here which also does all the linking for us.

D:\cc65>cd samples

D:\cc65\samples>dir

 Volume in drive D is Data
 Directory of D:\cc65\samples

26/05/2016  12:40 PM    <DIR>          .
26/05/2016  12:40 PM    <DIR>          ..
17/05/2016  02:26 PM             2,300 ascii.c
17/05/2016  02:26 PM             8,068 diodemo.c
17/05/2016  02:26 PM             2,455 enumdevdir.c
17/05/2016  02:26 PM             6,928 fire.c
17/05/2016  02:26 PM    <DIR>          geos
17/05/2016  02:26 PM             6,592 gunzip65.c
17/05/2016  02:26 PM             1,956 hello.c
17/05/2016  02:26 PM             3,772 Makefile
17/05/2016  02:26 PM             3,711 mandelbrot.c
17/05/2016  02:26 PM             7,345 mousetest.c
17/05/2016  02:26 PM             6,236 multidemo.c
17/05/2016  02:26 PM            69,766 nachtm.c
17/05/2016  02:26 PM             3,117 overlaydemo.c
17/05/2016  02:26 PM             8,573 plasma.c
17/05/2016  02:26 PM             5,865 README
17/05/2016  02:26 PM             2,876 sieve.c
17/05/2016  02:26 PM             5,269 tgidemo.c
17/05/2016  02:26 PM    <DIR>          tutorial
              16 File(s)        144,829 bytes
               4 Dir(s)  1,717,242,986,496 bytes free

D:\cc65\samples>cl65 -O -t c64 ascii.c

D:\cc65\samples>

Quickly check that there's a binary called 'ascii' in the folder with no extension.

D:\cc65\samples>dir

 Volume in drive D is Data
 Directory of D:\cc65\samples

26/05/2016  12:52 PM    <DIR>          .
26/05/2016  12:52 PM    <DIR>          ..
26/05/2016  12:52 PM             2,648 ascii
17/05/2016  02:26 PM             2,300 ascii.c
26/05/2016  12:52 PM             2,767 ascii.o
...

You've got a compiled application! Let's run it. Open up VICE (x64.exe is the c64 version) and choose File -> Autostart disk/tape image. You'll need to browse to where you compiled the sample and set the filter to all files. Once you see 'ascii' (or whatever you compiled) double-click it.

VICE-Select VICE-ascii-running

Feel free to play with the other samples and see what C code is available and explained.

Poking the Port

BASIC had two commands that altered system memory. PEEK and POKE were essentially READ and WRITE. They allowed the user to change values in RAM or read values back. Hitting certain addresses did certain things. Specifically, on the C64, POKING 56579 altered the input/output configuration of the User Port and then POKING 56577 changed the values being sent.

To do this in C, we need equivalent functions. Fortunately, the cc65 wiki has all the information we need. It turns out that no function is required, you can simply write to ports by setting the address (via a pointer) to the value you require. To make it a little less difficult to read, they've also provided macros to do the same thing. They'll help when you come back to the code 6 months down the track and can't read a thing you wrote!

    #define POKE(addr,val)     (*(unsigned char*) (addr) = (val))
    #define POKEW(addr,val)    (*(unsigned*) (addr) = (val))
    #define PEEK(addr)         (*(unsigned char*) (addr))
    #define PEEKW(addr)        (*(unsigned*) (addr))

There's also pokes for 'WORDs' there, but we don't really need them. Look here for a huge list of what you can PEEK and POKE. Turns out there's more memory address to poke here.

Note: These defines are also in peekpoke.h, just include that!

Moving the cursor

By default, as per CMD or any other Console, the text just rolls down the screen. Scrolling (with a buffer) is something that you actually need to implement in C64, so either start preparing for a buffer, or just get ready to use a single screen and clean up after yourself.

I want to draw a diagram of the 8 LEDs I'm about to control, so for this purpose we'll need to be able to place characters at certain positions. This involves moving the cursor to the required location and then outputting the character.

Fortunately, functions are already there to do all this... just use the gotoxy as per the source listing below.

Controlling the parallel port and 74HC595

So, we now have everything we need to write out the data required to control the 595. I'll just list it below. There's more information back here on how it actually works.

#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <joystick.h>
#include <peekpoke.h>

#define USERPORT_DATA 0xDD01
#define USERPORT_DDR  0xDD03

static const char Text [] = "Train Controller!";

void shiftout(int val)
{
	int i = 0, zzz = 0;
	for (i = 7; i >= 0; i--)
	{
		POKE(USERPORT_DATA, (val & (1 << i)) == val ? 1 : 0);
		POKE(USERPORT_DATA , 2);
		for (zzz = 0; zzz < 1000; zzz++) {}
		POKE(USERPORT_DATA , 0);
	}
	POKE(USERPORT_DATA , 4);
	POKE(USERPORT_DATA , 0);
}

int main (void)
{	
    unsigned char XSize, YSize;
    int i = 0, z = 0, zz = 0;

    //set all pins to output.
    POKE(USERPORT_DDR, 255);
		
    /* Set screen colors, hide the cursor */
    textcolor (COLOR_WHITE);
    bordercolor (COLOR_BLACK);
    bgcolor (COLOR_BLACK);
    cursor (0);
	
    /* Clear the screen, put cursor in upper left corner */
    clrscr ();

    /* Ask for the screen size */
    screensize (&XSize, &YSize);
	
    /* Top line */
    cputc (CH_ULCORNER);
    chline (XSize - 2);
    cputc (CH_URCORNER);

    /* Vertical line, left side */
    cvlinexy (0, 1, YSize - 2);

    /* Bottom line */
    cputc (CH_LLCORNER);
    chline (XSize - 2);
    cputc (CH_LRCORNER);
	
    /* Vertical line, right side */
    cvlinexy (XSize - 1, 1, YSize - 2);

    /* Write the greeting in the mid of the screen */
    gotoxy ((XSize - strlen (Text)) / 2, YSize / 2);
    cprintf ("%s", Text);
	
    /* MARQUEE */
    for (zz = 0; zz < 4; zz++) {
        for (i = 0; i < 8; i++) {
            shiftout(1 << i);
            for (z = 0; z < 8; z++) {
                gotoxy (((XSize - 15) / 2) + (z * 2), (YSize / 2) + 4);
                cputc((z == i ? 'X' : '_'));
            }
        }
        for (i = 7; i >= 0; i--) {
            shiftout(1 << i);
            for (z = 0; z < 8; z++) {
                gotoxy (((XSize - 15) / 2) + (z * 2), (YSize / 2) + 4);
                cputc((z == i ? 'X' : '_'));
            }
        }
    }

    /* Wait for the user to press a key */
    (void) cgetc ();

    /* Clear the screen again */
    clrscr ();

    /* Done */
    return EXIT_SUCCESS;
}

Note: some cputc calls reference defined characters such as CH_URCORNER, etc... These are PETSCII, the embedded character set of the Commodore. The wiki page has the numbers for the characters. Go to the include folder of cc65 and then cbm.h to see if the character is defined, otherwise just put the value in as a raw number.

And the result.. the bloody thing worked first go. Pretty scary actually... compiling and executing C code on the C64 was too easy. Of course, I cheated by using an SD2IEC. The load command was LOAD "0:TRAINCTL",8 followed by RUN.

DSC03724 DSC03723 DSC03725

You'll note that my keys are still on order... I can't wait for them to arrive as pressing 8 is tedious. Also that the last shot shows two Xs lit. Blame shutter speed and screen refresh times.

What's next?

Maybe it's time to hook up the serial port? nanoflite has provided a C driver which may well help us. Otherwise it's time to write a real railway controller for the parallel port interface... but I should actually finish that first.

26May/160

Commodore 64: Wiring up the User Port

The User Port on the Commodore 64 is a combined parallel port + serial port. Both of these can be used after a little wiring effort. Memotronics has the ports available, with nice handles. The handle is imperative for ease of removal. You really don't want to be pulling on wires to unplug anexternal device! I purchased this and also couldn't resist the 200 random LED bag.

DSC03680 DSC03681 DSC03703

User Port Socket Alignment

There's a row of letters and a row of numbers... make sure the numbers are along the top. Looking at the back of the C64, you'll notice there's two slots for socket alignment. To the right are two pins, then the center block, then 1 pin on the left.

DSC03705 DSC03706

Yes, the socket is plugged on upside-down in that middle picture!

DSC03711 DSC03712

The socket from memotronics comes with two white spacers in a plastic bag. Slide them into the slots appropriate for each side of the socket. Once done, tin up your wires and solder through to the parallel port.

Connecting it to our circuit

The whole reason behind this is to control the circuit described here and here. The goal was to use a standard interface, being the parallel port. The Amiga and the PC happily work; the Commodore was next. As mentioned above, the data pins are there, we just need to wire it through.

One really good point: once you've attached the wire to the parallel port end, draw a black line down pin one to the other end. Knowing which wire is which will get confusing once you've jammed it through the user port socket 'cap'. I actually surprised myself expecting to see the black line elsewhere and was glad I'd drawn it.

With the User Port socket in hand, I reviewed a few documents to work out which pins did what. All pinouts was a good source, until I realised that 'I' was missing. I assumed they got it wrong and so went searching further. The c64 Wiki has a pinout also, which also doesn't include 'I'. What's that old saying? No 'I' in 'Team'? There's no 'I' in 'User Port' either...

One quick check elsewhere at hardwarebook.info told me that the port I had from memotronics, which has an 'I', is incorrect. So... just make sure that the

The 'middle' 8 lower pins of the socket are the data pins that 1-to-1 match D0-D7 of the parallel port. You can then use Pin 1 and 12 to GND on the parallel (18-25) and yes, 17 is GND, but not on Amiga!

DSC03708 DSC03709 DSC03710

DSC03713 DSC03717 DSC03718

Putting it all together

Everything plugged together nicely... I powered up the circuit first; you should always make sure perpipherals are powered on first, prior to turning on the C64. Either way, LEDs lit as per usual and then it was time to hack in BASIC. You can see on the screen that I tried to output an 8-bit binary value to the 595... but the results weren't stable. It was late and I'd accomplished the first task: The pins were wired in and the LEDs between the optos and the C64 lit as expected!

DSC03720 DSC03721 DSC03722

What's Next?

Either I continue hacking BASIC and get the 595 driven, or I use something like cc65 and write real C. I think I'll go the latter. I also want to hook up the Serial port here... so I'll extend my little dongle to have a serial port hanging out of it also.

See this post on cc65 for an example of how to program the Usre Port via C!

26May/160

Parallel Port: Shift Registers

Now that we've controlled a LED by a single pin on the Parallel Port, it's time to expand our abilities. The goal of this post is to describe the usage of Shift Registers and then consume a minimal amount of pins, 3 out of the 8 available, to control up to 64 outputs!

A Shift What?

So, you have 8 digital input/output pins on the Parallel Port. This isn't the greatest amount and it'd be really nice to extend the amount available. There are a few ways, but a popular method is to employ a shift register.

A shift register is an IC which takes a 'serial' input and then outputs this on it's output pins in 'parallel'. By 'serial', we mean that the IC accepts input data on one pin only. This data is provided by you, as either a '1' or a '0'. Once you've set the value on the input pin, you then toggle another pin, known as the 'clock' pin, to tell the IC that the data is ready to be consumed.

The shift register, once the clock pin is toggled to a '1' and then back to a '0', will look at the input pin and then copy this value into it's internal register. We're using a 74HC595 which has 8 internal registers and therefore 8 output pins. At this point, the IC shifts all values on it's 8 internal registers up one slot and then sets the very first internal slot to the value you've provided.

You can then provide 7 more values, toggling the clock pin between each, an the IC will shift them along it's internal registers. What you've now done is set an 8-bit value inside this IC. Once the IC contains the value you've requested, you toggle the 'latch' pin and the IC will shift these 8 values out to the 8 output pins. These are known as 'parallel' as there is a single pin for each value; as opposed to the input which is serial (or one-after-another) on a single pin.

So, we've used 3 pins/wires here to control 8 output pins... pretty neat hey? We've turned our original 8 Parallel Port pins into (8-3) + 8... 13!

But wait, there's more! There's an 'output' pin on the shift register that reports the 'shifted off' value. What can you do with this? Feed it into a neighbour shift register as the 'input'. Hook up the same clock/latch wires as the first register and then, when you shift the 9th bit into the first, the very first bit you shifted in will be shifted out of the first register and into the second.

This means that, with the same 3 wires, you now have 16 outputs! Of course, you can keep doing this. I can't find anywhere that mentions how many you can effectively chain up. Of course, as you add more, the time to toggle out the values increases and you need to make sure that your code/hardware has time to think about other things instead of spending it all talking to the shift registers.

Connecting it to our previous circuit

We only used one line of the parallel port in the previous circuit and therefore only controlled one LED. For this first shift register, we'll need 3 lines. First thing is to hook up three LEDs with matching resistors. We could just hook two other lines up direct to our new shift register, but I like being able to visualise the data (and troubleshoot issues!)

Parallel-Port with 74HC595With this circuit, you'll be able to use the sample code in the previous post to control the 3 LEDs.

Sending out a 1, 2 or 4 should switch them on individually. Sending any combination of these, by adding them together, will turn on the associated LEDs. For example, sending out the decimal value 3 will switch on the first and second LED. The value 5 will switch on the first and third LED. As the value makes it to the port, it is split into it's individual bits, which are then translated to the data pins.

Once these are working, we're going to splice in some opto-couplers. We don't want any untoward voltages getting back to the parallel port. Optocouplers contain an LED and an internal light sensor. When power is applied to the input, the LED lights and the light sensor receives the signal. This is then output to the secondary circuit. This effectively provides an 'air gap' between the two circuits.

From these couplers we can control our shift register(s). Hook the three outputs to the 74HC595 shift registers SERIAL, CLOCK and LATCH pins. Remember the order as they each play key roles (as per the description of how they work above.)

Once you're ready, check that the three input LEDs react accordingly to basic Parallel Port data. Note that you may get erroneous data coming out of the shift register from the get-go. Data coming off the Parallel Port during system boot cannot be controlled and may cause havoc. We'll do something about this in a later article.

Building this required a LED array... you could do it easier and get one of those bar-graph arrays. Wiring up all the individual LEDs gets a little tricky.

DSC03682 DSC03683 DSC03684

Controlling the data output

Based on the initial description of the shift register, we know that we have to control the 3 data lines in a special sequence. First thing we need is an 8-bit data value to send out. Once we have this we can send each data bit out via the SERIAL line; toggling the CLOCK signal in-between. Finally, toggling the LATCH should see our value displayed in a glorious binary LED representation!

I've used Windows and the Parallel Port code here to manually try and turn on LEDs. My wires are hooked up as D0:SERIAL, D1:CLOCK and D2:LATCH. I am going to send 00000001 as the value to ensure that all LEDs are turned off bar the first.

  1. Ensure that LATCH is low (red)
  2. Toggle D0 to RED, this is a '0' for the first bit of the serial value.
  3. Now toggle the CLOCK (D1) on and off 7 times.
  4. Toggle D0 to GREEN.
  5. Toggle the CLOCK on/off once more.
  6. Toggle D2 on and off...

If everything is hooked up, then you should now have one LED showing on the output of the 74HC595. It didn't quite work with this initial circuit... LEDs would light a little randomly.

DSC03685 DSC03691 DSC03693

Noise

Although the above circuit worked, it was not reliable! Every now and then one LED (or two) past the one I wanted to switch on would also switch on! Sure, my soldering was dodgy, let alone the wiring also being messy. Either way, noise was being introduced and the flipping of the serial and clock was jumbled, causing random output.

The solution to this was to put a 100nf capacitor across the +5v and gnd supplying the 74hc595. This cap should be put as close to the IC pins as possible. Once in place, this stabilised the data from the PC Parallel port.

References

- HowTo: Understand the 74HC595 (David Caffey)
- Gammon Forum: Using a 74HC595 output shift register as a port-expander
- protostack: Introduction to 74HC595 shift register – Controlling 16 LEDs

What's Next?

Next is a 12v throttle. I intend on using the described sample here. The only issue is that it wants a potentiometer to vary the output voltage. This is an analogue method; we'll convert it to digital by calculating 8 resistor values to imitate the throttle at 8 positions.

I'll write this up soon once I've completed the circuit.

18May/160

Commodore 64C: An Introduction

I've had a hankering for an Amiga for a while now... I don't know why.. I think it has something to do with the accelerator that was made for the Amiga 600 that made it go faster than ever thought possible... sure they emulate the entire core in a much faster CPU and high-jack the motherboard... but it's still an awesome feat!

I also have a feeling that I missed out on a whole realm of computing in my childhood by following the Nintendo/IBM route. I'm pretty sure there was a-whole-nother world out there... Sega, Amiga, Atari, TI, etc... that I never got to sample. Actually, I lie... my neighbour had a TI-99/4A, followed by a Sega Master System... but I only ever got to play them briefly. Hunt the Wumpus was random, but fun at the time!

My first Commodore

Officially, this is my second. The first was purchased in the mid-90s at a fair at high school, but didn't work. It was the original breadbox style and was stone dead. Should've left it in the garage!

Anyway, this past weekend I spied a box of junk at the local Trash and Treasure and actually thought I saw an Amiga 500/1200. Instead it turned out to be the 'newer' C64. I was hooked.

DSC03591 DSC03592 DSC03593

The guy wanted $100 for the box. It contained:

  • C64C (missing 3 keys) + Power Supply
  • Composite Video Cable with Stereo Audio
  • Two quickshot joysticks
  • One control pad (looks like a NES clone)
  • 3 odd cartridges
  • Another odd console named 'Tempest'
  • 1571 External Floppy Drive

Now that I list it, it could well be worth the AUD$100. I offered $50. He haggled back to $60... knew I had him. Asked if it worked... he had no idea... so I pulled change from my pocket and made a deal at AUD$52.

Bargain.

Does it work?

Worked first time... plugged it in and switched to A/V input... blue screen of dea... BASIC!

DSC03594 DSC03595 DSC03596

Random 4s and 8s on the screen... and the cursor was stuck hard-left. This directly mapped to the keys that were missing. 4, 8 and HOME. Turns out that there is no spring when the keys are off, so these were all 'pressed'.

DSC03629 DSC03624 DSC03625

Got some tape, placed along the shaft of the missing keys to hold them in the air. Restarted... it works! Quick search on eBay on my phone, from the couch, in front of the LCD TV that was happily displaying technology from 1987. Found replacement keys... ordered them on the spot.

Next google to a sample BASIC app... found a tone generator. Tedious data-entry thanks to taped-keys... but it worked beautifully through the surround system. Hah.

How much more powerful is my phone than this? No idea.

1541-II Floppy Disk Drive

This is external and has its own brick of a power supply. Turns on and hums away when I attempt to access a disk... so I assume it's in working order. There's disks on their way via eBay, so I'll test it out shortly.

DSC03599 DSC03600 DSC03602

The innards

See below for the case opened. I was curious as to why it had a green LED. Turns out Commodore could never work out what colour the LED should be. The warranty seal was also very well voided. Seems to be some newish solder around the PAL/NTSC circuitry... maybe this was a conversion?!

Turns out I have an Assy 250469 Rev. B. Built in 1990 (assumed by the scratched out '91' on the RF Modulator.) This model was still being built in the '92, so I seem to have acquired one of the final models.

DSC03608 DSC03610 DSC03612

DSC03614 DSC03616 DSC03617

DSC03621 DSC03620 DSC03618

What's next?

  1. Yes, that's a model railway under the C64C... I will control it. Turns out I bought a book when I was young that was all about robots and the Commodore 64. I need a VIC-REL or equivalent. Bugger that... let's find the header for the user port and make my own!
  2. Buy floppies (thanks eBay) and test out the drive.
  3. Buy some form of flash disk and play all the games I missed out on. I chose the SD2IEC... waiting for it to arrive.
  4. Serial port? Modem? Ethernet?
  5. What else?

This is fun!