Cloud Enabled Commodore 64: Part V – Do It Yourself

By now many people saw the demo of the Cloud Enabled Commodore 64 project, read posts discussing implementation and the retrospective and some commented that they would like to try it out themselves. This post describes how to do that.

We will start from listing required hardware and then will move to the required software.

Hardware

There are two hardware options to try the project out – you can either use an emulator or use a real Commodore 64. The emulator route is a bit easier as it does not require a working Commodore 64 and additional peripherals. You will still need a Node MCU board like this:

Node MCU Board
Node MCU board

which you can get on ebay for below $5. If you decide to try it out on a real Commodore 64 you will need a C64 WiFi modem. Make sure it is using the NodeMCU module and that the module is accessible. This is how mine looks like:

C-64 WiFi Modem

For the real C-64, you will also need to be able to load the cross-compiled program to your C-64. There are a few possibilities here – I used an SD2IEC floppy drive emulator, and it worked great for my needs.

Software

Before moving to software I would like to start with a disclaimer. I did all the work on MacOS. I will try my best to provide instructions for Windows and Linux, but they might be lacking.

Git

You will need git to clone the project repo. It is very likely that you already have git installed on your machine but if not follow instructions from here: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git

make

You will need make to build the project and the cc65 toolchain. On MacOS you can get it by installing Apple developer tools. On Linux, you likely already have it. On Windows you would need to install either Cygwin or use WSL (Windows Subsystem for Linux).

cc65

cc65 link is a “cross development package for 6502 systems”. To get it, follow instructions listed on https://cc65.github.io/getting-started.html. Please make sure the tools can be resolved (e.g. run sudo make avail or add them to the path). You can test your installation by running cc65 from command line and verifying that it printed cc65: No input files.

Arduino IDE

We will need to update the NodeMCU board for which we will use the Arduino IDE. It can be downloaded from https://www.arduino.cc/en/software.

VICE emulator

If you are going the emulator route (which I recommend even if you eventually want to use the real C-64) you will need the VICE emulator which you can download from: https://vice-emu.sourceforge.io/index.html#download

Dotnet

You will also need the .NET SDK. It will be used to run the server locally. This makes it easier to test and troubleshoot, if necessary. It is will also be needed if you decide to publish the server to Azure. You can get the .NET SDK from https://dotnet.microsoft.com/en-us/download

Node and npm
The server contains a web client which depends on a few node packages (most notably the @microsoft/signalr package) so you will need npm to install these packages.

Preparing and running the application

With all pre-requisites installed we can get down to business and try to start the application. Here are the steps:

  1. Clone the project repo
    Run git clone https://github.com/moozzyk/SignalR-C64

  2. Start the TestServer
    The test server is the chat server our chat application will be talking to. Note, that the server registers an https endpoint which on will use a developer TLS certificate when running locally. This may result in showing a warning or asking to register the certificate (which you can do by executing dotnet dev-certs https --trust). If you don’t want to see the warning you can remove the https url from this line and just use HTTP. This will work fine for local runs but is not recommended (perhaps not event possible) when running the server on Azure. You will also want to make sure that the server is accessible from outside of your machine (i.e. make sure that other devices on your network can access the application).
    To start the TestServer you need to go (cd) to the TestServer directory and run:
    npm install
    dotnet run

  3. Verify that the test server works
    Connect to the server using a browser. Ideally you would want to connect using an external IP or the name of your machine (i.e. avoid 127.0.0.1 or localhost) or use another device connected to the same network. Once connected try to send a message – if you receive the message you typed, the server is set up correctly. (Note that if you try connecting to the server with HTTPS you may see warnings caused by using the local (dev) TLS certificate.)


  1. Backup the C64 WiFi Modem firmware (optional)
    If you are using the C64 WiFi Modem for this project you may want to back the currently installed firmware up as the next step will overwrite the firmware effectively removing the original functionality provided with the modem (i.e. connect to BBSes). One way to do this is to use the esptool to download the existing firmware and then upload it later to bring back the original functionality. You can install the esptool by running:
    pip install esptool

    To download the firmware connect the modem to your computer and run (remember to update the port to point to your serial device):
    esptool.py --baud 115200 --port /dev/cu.usbserial-1420 read_flash 0x0 0x400000 ~/tmp/C64WiFi-backup-4M.bin

    To upload the firmware back to the board run:
    esptool.py --baud 115200 --port /dev/cu.usbserial-1420 write_flash 0x00000 ~/tmp/C64WiFi-backup-4M.bin

    You can use screen (or Putty on Windows) to test that the firmware has been uploaded correctly. First run:
    screen screen /dev/cu.usbserial-1420 300
    and then type AT? You should see something like this:
    AT?
         cOMMODORE4EVER V2.3 wIFI mODEM
    ...

  1. Upload the firmware to the NodeMCU board
    – Start Arduino IDE and open the EspWs.ino sketch and set the default credentials on this line. (For simplicity, the code running on C-64 does not allow setting credentials – it assumes that the credentials are properly configured and will just initiate WiFi connection.)
    – If you are planing to use real C-64 set the transfer speed to 600 bauds here. Leave 1200 if using the emulator as Vice-64 does not seem to support 600 bauds.
    – Connect your NodeMCU board (or the C64 WiFi Modem) to your computer.
    – Make sure to select the NodeMCU 1.0 (ESP-12E Module) board (If you can’t see this board you may need to add it first using the Board Manager Tools -> Board Manager, search for “esp8266” and then install)

– Select the device

– Upload the firmware to the board

  1. Verify firmware was deployed successfully
    Go to the EspWs directory and run the following command (make sure to provide correct values for the server, device and transfer rate):
    python3 prototype.py 192.168.86.250:5000 /dev/cu.usbserial-1420 1200

    If the firmware has been uploaded correctly you should see the following output:
    b'\x03\x00'
    OK
    b'\x05\x1dws://192.168.86.250:5000/chat'
    WS
    b'Connected'
    b'\x06*{"protocol": "messagepack", "version": 1}\x1e'
    OK
    DATA
    b'{}\x1e'
    DATA
    b'\x02\x91\x06'
    DATA
    b'\x02\x91\x06'

    Note there will be some delay before you will be able to see most of the output as it takes about 10 seconds for the board to connect to WiFi. Another, important thing is that if you stop the script and want to try again, you’ll need to reset the board (press the RST button on the NodeMCU module and wait a few seconds before trying again).
  1. Configure Vice
    This step is only needed if you want to run the app using the Vice emulator.
    Open Vice and go to Settings -> Peripheral devices -> RS232
    Make sure to “Enable Userport RS232 Emulation” and select the device that you want to use. In the RS232 devices you need to provide the device filename and the transfer speed. For the emulator you want to 1200 bauds. Here is how this is configured in my case:

  1. Configure chat server URL
    You will need to set the correct URL to be able to connect to your chat server by modifying the value here.
  2. Build and run
    The application should now be ready to run. Go to the App directory where you will be able to build the application with make. The makefile supports a few targets. The default target (i.e. running make without any arguments) will compile the app to a.prg file. make clean will delete temporary files. make d64 creates a .d64 (disk image) file you can either to attach to the emulator or use to run on a real C-64 (e.g. using SD2IEC). The fastest way to build and run the app on the emulator is to invoke the following command:
    make clean && make && x64sc --autoload signalrdemo.prg
    It will clean temporary files, create a prg file, start the emulator and automatically load the prg. Then you can just type run in the emulator to run the app.
  3. Deploy the server to Azure (or a cloud provider of your choice)
    If the application is working correctly in the local environment you can deploy the server to a cloud provider. You will need to update the URL accordingly and compile with the new settings (steps 8 and 9).

This post concludes the Cloud Enabled Commodore-64 mini series. I hope this project brought back some good memories for you as it did for me.

Cloud Enabled Commodore 64: Part IV – Retrospective

In the previous post we looked at implementation details of the Cloud enabled Commodore 64. In this post I would like to sum up the project and retrospect of what I might have done differently.

In general, I am extremely happy about how the project turned out. First and foremost – it does work! I was able to put all technologies together and line all the stars up to the point where an almost 40-year-old computer is talking to cloud and thus can connect to other – more modern – devices.

I also have to admit that I am amazed how great the Vice emulator is. I was not confident that it would be possible to implement the project end-to-end on an emulator and – even if it were – that the code would run on the actual hardware without any additional debugging.

Having said that after looking back at this project I found a few things I might have done differently.

Time management

I have not put any timelines on this project. I worked on it only on and off – when the time allowed, and when I felt like it. As a result, it took almost a year to drive it to completion. This was only a hobby project so it is not a big deal, but I feel that if I were more focused, I could have finished it in half of that time.

Use the C language instead of assembly

When I embarked on this project, I decided to solely use assembly for the Commodore 64 code. Halfway through I looked more at what the cc65 had to offer and pondered moving to C. There were a few downsides to this approach like the ramp up time or having to figure out how to mix assembly and C especially in the context of interrupt handling. I also was concerned about the size of the binary produced by the compiler. This was probably unreasonable as I did not include any artifacts like graphics or music. Switching to the C language could increase my productivity in the long run, reduce the number of hacks I implemented (especially towards the end of the project) and make the code more accessible.

Run the SignalR client on the board instead of on the C-64

One of the principles of the project was to use the NodeMCU/ESP8266 board only as a simple network card and have the C64 run everything else. I felt like pushing more logic to the NodeMCU board would be “cheating”. Relying more on NodeMCU/ESP8266 could have a couple advantages:

  • having to write much less 6502 assembly and, as a result, potentially finishing the project faster
  • a general use SignalR client in C oriented towards embedded systems

Run serial communication at more than 1200 bauds

To simplify the project, I decided to run communication between C64 and the C64 WiFi modem at 1200 bauds. This is really, really slow. If “1200 bauds” does not tell you much: this is roughly 120 bytes per second. C64 can potentially support up to 2400 bauds. The C64 WiFi modem supports speeds up to 9600 bauds. To run at these speeds, I would have to use special routines for handling serial traffic. This would bring the transfer to almost 1KB/s. To be honest, given today’s transfer speeds, I don’t think achieving 9600 bauds would make any difference. This project does not and will never have any practical use, so 1200 bauds is probably as good as 9600 bauds. We also only have less than 60 KB of memory to fill (after disabling all ROMs).

Addendum

There is a quite hilarious twist to the paragraph above. I wrote it after briefly testing the project on the actual hardware but before making the video I posted on youtube. When I was shooting the video I started seeing garbage on the screen. It started innocently – apparently the exclamation point was not shown properly. I immediately knew something bad was happening because I did have a dedicated code to handle punctuation marks. Nevertheless, I ignored the error hoping it won’t get worse. Unfortunately things went south really quickly – see for yourself:

I took a break to debug the issue and was only able to reproduce it on the actual Commodore 64 but never on the emulator. I concluded that I was hitting one of the bugs in the KERNAL code handling serial communication which would result in occasionally flipping bits. Indeed, after lowering the serial connection transfer speed from 1200 bauds to 600 bauds the problem disappeared. So, for the demo, I ended up with 600 baud for the real C-64 and 1200 baud for the emulator as the emulator did not have any issues at “higher” speeds.

These are my biggest take aways from this project. In the next post I am planning to provide steps to anyone interested in trying this project out on their own.

Cloud Enabled Commodore 64: Part III – Implementation

Once I had a reasonable development environment, I could get my hands dirty and start coding. I decided to re-use a server from one of my other projects, so I only needed to take care of the client-side implementation. It consisted of two main parts:

  • a library running inside the C64 WiFi Modem responsible for networking
  • a chat app running on Commodore 64

Let’s take a closer look at how they were implemented

Networking with the C64WiFi Modem

The modem is responsible for handling network related functionality. It needs to be able to connect to the WiFi network and – once connected – allow to establish a connection with a web server. This functionality is provided by the ESP8266 chip that powers the C64 WiFi modem. The WiFi connection can be established using the ESP8266WiFi library. HTTP requests can be sent to a web server using the ESP8266HTTPClient and the WebSocketClient can be used to communicate with the web server over a webSocket. To expose this functionality to external devices (clients) I created a library running on the C64 WiFi Modem (or, to be more accurate, on the ESP8266 chip) that implements a simple protocol that allows to start or stop WiFi, open a webSocket and send and receive messages over the webSocket. The client would send commands using the serial device and would receive a response containing the result of the operation. Once the webSocket is opened successfully, the client can also instruct the modem to send data to web server or receive data from a web server. The functionality offered by the library running on the modem is not geared towards any specific use case. In fact, testing it from a C-64 would be quite daunting and it was much easier and faster to create a Python client that used the pyserial package to communicate with the modem.

Commodore 64 chat app

The chat app for the C-64 is much more complicated than the library for the C64 WiFi modem. It not only needs to provide the interface for the user but also takes care of all the communication. As I set out to start working on the implementation, I was worried that coding all of this up in assembly will end up in a horrible spaghetti code that no one – including myself – will be able to understand. To prevent that, I decided to go with a layered design where each layer is responsible to handle a single concern and can only communicate with the layer directly above or below. I identified the following layers:

          +----------------------+
          |     application      |
          +----------------------+
          |       SignalR        |
          +----------------------+
          |         ESP          |
          +----------------------+
          | serial communication |
          +----------------------+
                    ...
          +----------------------+
          |    C64 WiFi Modem    |
          +----------------------+

The serial communication layer is responsible for communication with the serial device – it knows how to open the device and allows reading incoming data. The ESP layer understands the protocol implemented in the library running on the C64WiFi modem. It knows how to create and send commands and interpret results. The SignalR layer uses the API exposed by the ESP layer to connect to WiFi, start a webSocket connection to a web server and communicate over this webSocket. It also has some understanding of the SignalR protocol – it knows how to initiate the SignalR connection, handle handshake or ignore “uninteresting” (or unsupported) messages from the server (e.g. pings). Finally, the application layer drives the execution of the entire application. It does that by setting up a raster interrupt which ensures that the application logic will be called repeatedly (60 times per second on NTSC systems, 50 times per second on PAL systems). Each time the interrupt handler is invoked it checks if there are any incoming messages and, if so, shows them on the screen. The interrupt handler also scans the keyboard and will take care of sending a message if the user pressed <RETURN>. All the code responsible for UI is encapsulated in a dedicated UI module. Sending and receiving messages directly in the application layer is a bit messy because it also includes logic that encodes and decodes messages according to the SignalR protocol and the MessagePack format. As per the layering above this should ideally be part of the SignalR layer but doing this in the application layer proved to be easier to implement and faster to run (important due to the timing constraints related to running inside the raster interrupt handler which needs to finish within at most 16 ms). Given this was just a hobby project, I decided that this trade off was acceptable.

In addition to the layered design, I also settled on using several patterns that helped me avoid mistakes and a lot of debugging:

  • arguments are passed to subroutines in registers, if possible
  • subroutine results are passed in registers, if possible
  • subroutines are not expected to preserve registers – the caller is responsible to preserve registers if needed
  • Some zero-page locations have a specific purpose (e.g. $fb/$fc vector always points to the send buffer) while other (e.g. $fd/$fe) can be used for any purpose and by any subroutines so no code should use them without proper initialization

Pivots

During the implementation I found that the project would get significantly easier if I changed some of the assumptions I originally made. Initially, I planned to communicate with the server using the long polling. Long polling (a.k.a. Comet) is a technique where the client sends an HTTP request and the server keeps it open until it has anything to write, or timeout occurs. Once the HTTP request is closed the client immediately sends a new HTTP request. This pattern is repeated until the logical connection is closed. Sending data to the server requires sending a separate HTTP request. When researching this option, I found that the ESP8266HTTPClient is blocking and does not allow working with more than one connection at the same time. I needed something better and I found the WebSocketClient library. Using webSockets was a much better option and saved a ton of work. I no longer had to deal with coordinating and restarting Http requests (which can get tricky) and establishing the connection to the server was greatly simplified as SignalR requires an additional negotiate request for the Long Polling transport but not for the webSocket transport.
I also decided to switch to a binary SignalR protocol. Out of the box, SignalR uses a JSON based protocol. This makes using it from JavaScript extremely easy. For other languages it usually does not make much difference as JSON support is widespread. JSON however is not a good fit for assembly. Even if I found a parser, I would only use it if I did not have any other option (the size of the parser and the speed of parsing were some of the concerns). Fortunately, SignalR also supports encoding messages as binary data using the MessagePack format. Binary format was a much better option for what I tried to do. First, the messages are much smaller. This was important because I decided to support only payloads of up to 256 bytes since 6502 has only 8-bit registers. (One of the consequences of “8-bit registers” is that indexing memory chunks of up to 256 bytes is easy with the Absolute Indexed addressing mode while working with bigger buffers is much more involved.) Second, parsing binary messages is much easier than JSON encoded messages as there is only one way to encode the message. I also only needed to implement a small subset of MessagePack features to be able to support my use case.
There was one more place where I switched from text to binary. The protocol I created to talk to the C64 WiFi modem was initially text based. The biggest advantage was that I could test it without using any specific tools – I would just start screen or PuTTY and could type commands directly from the keyboard to see if things work. Interpreting data received from the modem in assembly turned out cumbersome (but worked!) but after I decided to move from JSON to MessagePack using a text based protocol for the communication with the modem was no longer an option. I had to create a couple of tools in Python to make testing easier but the simplification it yielded in the chat app was totally worth it.

The implementation for the Cloud Enabled Commodore 64 required aligning many stars. Keeping clear boundaries, using the right tools and a bit of luck was essential to complete the project successfully. Revisiting assumptions, adjusting the direction and finding simpler solutions cut a lot of time and effort. Next time we will take a look at more ideas I had and how they could have shaped the project if I decided to use them.

Cloud enabled Commodore 64: Part II – Development Environment

After devising a high-level design for my idea, I needed to take care of more mundane things, starting with setting up the development environment. First, I had to find out if using a Commodore 64 emulator for testing and development was an option. I wanted to rely on the emulator as much as possible to speed up the development time. The main two concerns I had were:

  • would the emulator even support external devices?
  • if, the above was true and my idea worked on the emulator, would it work on real hardware

Vice-64 was the most advanced and mature Commodore 64 emulator I knew of. I used it previously in some of my projects (e.g. Vintage Studio) and to occasionally play some of my favorite games but never with any external hardware. Looking at Settings gave me hope – I found that there were at least controls allowing to configure serial communication. After playing with the settings for a bit I was able to make Nova Term connect to a BBS via the C64-WIFI modem connected to my laptop via USB. This answered my first concern and assured that I would be able to code and test the entire solution on my laptop with maybe some additional debugging on the real C-64 at the very end. Another benefit of using Vice was access to the built-in debugging tools. They allow setting breakpoints and stepping through the code and the support for labels makes debugging much easier.

Next, I had to decide on which 6502 assembler to use. I found ca65 (a part of cc65 suite https://github.com/cc65) to be the best option. It has a lot of great features, has been around for a long time and has a lot of documentation as well as source code available on github. I was almost sure that VS Code was the safest bet to serve as my 6502 assembly editor as it has a ton of extensions. The main thing I was looking for was syntax highlighting and I quickly found the CA65 extension which did exactly what I was after. The extension also provides build tasks but I have not used them as I decided to go with make. Overall, the combination of Vice, VS Code, ca65 and make ended up making quite a productive environment where I could quickly build, launch and debug my project.

To handle the NodeMCU part I decided to stick to Arduino Studio. I knew that VSCode had an Arduino extension (heck, I even created one long time ago before Microsoft decided to occupy this space) but it tried to reformat my code the way I did not like and I didn’t want to spend the time on adjusting it to my liking. I also think I had some issues with uploading my sketch to the board while everything worked fine from Arduino Studio.

At the beginning I was able to test my NodeMCU code using screen. It was possible because the protocol I implemented was text based. Later, I switched to a fully binary protocol and using any general purpose solution was not an option, so I created a simple terminal in Python that was able to interpret and translate my commands and the responses from the module.

I used a standalone NodeMCU module for most development but occasionally tested my code against the C-64 WiFi’s NodeMCU module. Surprisingly, I found some slight differences between how the boards behaved immediately after booting and had to implement a simple workaround which ignored first few bytes received from the module.

For the SignalR part I just took the chat server I created for my other project. I ran it locally during the implementation and then deployed to Microsoft Azure for final testing and demo.

The final missing piece of my environment was actual hardware. I had a Commodore 64 that I know was working the last time I turned it on because it was when I fixed it. I also had a 1084S-D2 monitor which stopped working when I was testing my C-64. Fortunately, it turned out to be only the power switch. Replacing the switch brought the monitor back to life. I decided to go fully retro and had to acquire a 1541 disk drive – luckily I found a working one in decent condition on craigslist. I received a bunch of disks used with Commodore 64 from a colleague many years ago. Despite all these years in a closet almost all of them worked just fine. The only thing remaining was to transfer the compiled program to a disk which I did using a SD2IEC module.

Looking back I am really amazed how many technologies – both hardware and software – were involved in putting my solution together. Four programming languages, vintage and modern hardware, embedded programing, Cloud technologies and a variety of, mostly open source, tools. All this made this project a lot of fun. Next time we will take a closer look at the implementation.

Cloud enabled Commodore 64: Part I – Introduction

This is the first post in a series that talks about my recent project dubbed “Cloud enabled Commodore 64”. The project is an attempt to connect Commodore 64 to Cloud (Azure) and let it communicate with a variety of clients – both modern and vintage. Here is a demo of the final result:

I had the idea of connecting Commodore 64 to Cloud in my head for a really long time. When I initially tried to take a stab at it, it was apparent that I didn’t have skills necessary to build the hardware. When the C64 WiFi modem came out I have already moved on and did not pay attention. Until that one, dark autumn Friday night, when I was browsing ebay and came across a C64 WiFi modem which I promptly bought. To be honest I did not even spend too much time looking at the pictures and when it arrived, I was very surprised to notice that the modem is just a NodeMCU Esp8266 board that can be plugged in to the C64 User port. This was a very pleasant surprise because I was already familiar with NodeMCU – I used it for one of the first ASP.Net Core SignalR demos.

The main purpose of the original C64 WiFi modem and its firmware was to allow Commodore 64 owners access BBS’es around the world with software called Nova Term. This did does not appeal to me at all. Due to the times and place I grew up in I did not even have access to a landline and the only way to get new software was via swapping with other enthusiast either at school or over mail. I did spend a night playing with the modem and figured one can use this modem to connect to a BBS directly from a Mac or Linux using screen (or a PC using Putty). This convinced me that the modem works and that my idea suddenly became real.

How? – High level design

The modem pretty much dictated the design. The communication between C64 and the modem would use Serial protocol and the modem would require a firmware that would deal with all network related affairs.

Cloud enabled Commodore 64 design

I decided on a few principles upfront:

  • Modem firmware will only deal with network (i.e. will not include client specific logic)
  • C64 software will be written in 6502 assembly
  • C64 software will use built-in Serial routines (KERNAL)
  • I will use Azure as the cloud provider and will use SignalR

I decided to use 6502 assembly to remember the fun I had using it on my first computer when I was a kid. Right decision or not, it made me appreciate how much progress have been made with regards to programming since then.

The KERNAL routines for Serial protocol are infamous for their bugs but they work OK at low speeds. There are several routines that patch and fix KERNAL code responsible for serial communication and allow reliable transfers at 2400 or even 9600 bauds. After I started investigating, I quickly realized that researching this subject would be interesting but also very time consuming. While it would be nice to have faster serial communication, running at 1200 bauds felt to be fast enough for this project – let’s be honest, in today’s world 9600 bauds is as fast (or as slow) as 1200 bauds and I never thought this project could have any practical usages.

Finally, I decided to go with Azure simply because I am much more familiar with Azure than AWS or Google Cloud. I am also quite familiar with SignalR and how it allows building engaging demos with the canonical example of a chat app being something that might even seem a legit use case for a 40-year-old computer.

These were my thoughts before I embarked on this project. I will however revisit some of these decision in a future post where I will reflect on surprises I encountered and what I could have done differently.

Why?

As noted before – this project was never supposed to have any practical usages. I just thought it would be a fun hobby project. And it really was. Seeing it working live on a real hardware was a blast.

That’s it for today. Next time I will go over the environment I used to develop this.