NEWS, EDITORIALS, REFERENCE

Subscribe to C64OS.com with your favorite RSS Reader
September 20, 2019#88 Technical Deep Dive

C64 OS Networking and Chess

Post Archive Icon

Welcome back dear readers. Let me start with some quick updates. I didn't put out a post in August, and I hate missing my deadlines. I took a week off in August to go off grid and spend some time with my family at a cottage. Rather than having a couple of smaller posts, I spent a lot of time working on a rather large reference document, the SD2IEC User's Manual. I worked on it for about 6 weeks, but just didn't have enough time to finish before the end of August. I posted the it to the blog on September 3, 2019. It's a doozy of a post. The original sd2iec github readme (upon which it is loosely based) was no slouch at 7,000 words. But at 28,000 words the SD2IEC User's Manual is 4X the size. I drew on the CMD HD User's Manual for inspiration in the syntax and BASIC and JiffyDOS examples. And I added a lot of descriptive content that was simply not present in the original. It is my hope that my work will become the go-to reference document for all SD2IEC users out there. Please link to the document and tell people about its existence.

It may seem odd that I post full user manuals to what is ostensibly a blog. However, I mentioned a couple of posts ago that I've introduced a new layout for navigating and finding the blog posts. The new View By Categories organizes the posts into categories based on how they've been tagged. This allows me, for example, to pull all the "Reference" documentation posts out and collect them together. The SD2IEC User's Manual can be found there now, side by side with other reference documentation posts, such as: C64 KERNAL ROM: Making Sense, the 6502/6510 Instruction Set, Commodore Hardware Information and reference tables for PETSCII, Screen Codes and key mappings.

Shortly after tweeting about the new SD2IEC user manual, Jérémie Marsin of Double-Sided Games recommended that to improve site SEO and general usability I should consider making the URLs of c64os.com friendly. Up until this point, posts have been referenced by their ID in a GET variable. Other parts of the site, the Buyer's Guide, C64 Luggable, and C64 OS documentation also exposed GET variables to access their subpages. I have plunged into Apache's MOD_REWRITE and manually updated a number of hardcoded links. The whole site now has more human readable and user friendly URLs. (The old urls, of course, continue to work. So any external links to my site won't break.)

In C64 OS news, I have recently got splitscreen mode actually working! Something I started talking about earlier this year. I've got it on my short list to write an in-depth technical post about what I learned and what I had to do to make a stable raster interupt that can be moved arbitrarily by dragging the status bar with the mouse. Look forward to reading about that.

I have also completed the work on getting the system-wide Utilities menu working. I had some original plans for how to implement it, thinking I didn't want to waste much memory. But I ended up with a solution that actually resulted in decreasing the memory footprint of the menu system in the process of adding support for the utilities menu. I've got another post planned to go into some detail on how this worked out. It's pretty cool.

And now on to the topic of today's post, C64 OS Networking and Chess.

 

Chess

During my aforementioned vacation to the family cottage, I spent a good deal of time playing chess with my son. So I got the bug to try to design a chess game for C64 OS.

Opinions seem to be mixed about whether this is a waste of my time or not. Some people have wondered what the point is of writing Chess for C64 OS, but others understand the point right away. Like this guy:

Translation from Italian: Like any self-respecting operating system C64 OS will have a nice chess game in high resolution.

Along with all my notes about the functionality I need to write, I also have a games section. Believe it or not, chess is the first and only game I had listed. I got the idea from 6502.org, which has the source code to a 6502 implementation of MicroChess by Peter Jennings. It even has an adaptation for playing over a 6551-based RS232 serial connection. Since the day I saw that, I knew I wanted to have a network-based chess game for C64 OS.

Now, why make chess for C64 OS? Besides to scratch an itch, what purpose can its development serve? First of all, an operating system is not just a program, it is a platform upon which other programs can be built, faster, more easily, and with more power and UI-consistency than writing a program from scratch. And the more applications (and utilities, and drivers, and so on) an operating system has, the more useful it becomes.

Network chess is the perfect candidate for C64 OS. As Progetto Zzap! (Project Zzap!) points out every self-respecting OS needs a chess game. Mac OS X has come bundled with a beautifully rendered 3D chess game since version 10.2, which itself derives from its OpenStep predecessor.

3D Chess for OpenStep.

Playing chess with a mouse is much more intuitive than with keyboard input only, and much faster than with joystick input. C64 OS happily provides the mouse input and event model for free. I recently completed the system-level split-screen and full-screen graphics modes. I'll be posting a technical deep dive into split-screen in the near future. Chess gives me the opportunity to show how programs with a graphical UI can be integrated into the predominantly text-screen based C64 OS UI. Additionally, I need to start working on the networking components of C64 OS. To do this, I need to devise a useful, thin, abstraction layer for applications to open a communications channel that supports numerous underlying network devices and hardware interfaces.

Having an application with real and practical needs makes it easier to wrap my head around what a network abstraction layer has to be able to do. Chess is a good level of complexity. It's a two-player game. It has a graphical display but is mostly static (that is, it's not an action/arcade game). It's turn based. Plus, there are opportunities for multiple levels of asynchronous communications, such as allowing the players to chat while the turn based exchange of piece moves is taking place. It's good. It's a good challenge, without being too hard to implement.

The Chess Board

Let's dig into chess itself for a bit first. It's a two player game, played on a checkered board of alternating light and dark squares. The board consists of 8 rows and 8 columns. Each player starts with a set of 16 pieces, made up from a total of just 6 piece types:

  • Pawn (X8)
  • Rook (X2)
  • Knight (X2)
  • Bishop (X2)
  • Queen (X1)
  • King (X1)

I'm not going to explain how to play chess, but it's good to review these details so we can consider how the board and pieces may be fit into the C64's graphic limitations. If we could manage to use hires mode (320x200) I would opt for that first. The higher resolution allows us to make pieces with more definition. Multicolor would give us more options for color, but at a loss of 50% of the horizontal resolution.

You have light pieces (white, but could be represented by some other light color) and dark pieces (typically black.) You also have light and dark squares. Light pieces need to be able to appear on both a light square and a dark square. Just as dark pieces need to be able to appear on light and dark squares. In both hires and multicolor modes colors are restricted by 8x8 pixel cells. In hires any two colors (of the 16 total) but only two colors may appear within a cell. This matters because you can't just make the board size any old number that's divisible by 8. We have 200px of height. So, we could make the board, say, 144 pixels high. And make it 144 pixels wide to make it square. When you divide 144 by 8 you get 18 pixels. That's a nice round number, but the problem is, it doesn't align with the underlying 8x8 pixel cells. This has consequences both for color limitations and for how you would draw a piece onto the board. Drawing would be slow, but the color limitations might actually make such an arrangement impossible.

Instead, we have to think about it aligned to the 8x8 cell grid. We have, then, 40 cells wide and 25 cells high. We could make the board 24 cells wide and 24 cells high. That would fit, everything would align, and each square would be exactly 9 cells (3x3). Pieces could be drawn with some nice detail, constrained to 24x24 pixels. That would work. But, the board would go almost edge to edge vertically. It wouldn't give us much room for seeing the board and part of the split-screen text-mode UI at the same time. I opted instead for the board to be 16x16 cells. Each piece then gets 2x2 cells, or 16x16 pixels. And we use ~2/3rds of the screen. Leaving ~1/3rd of the screen available for pulling the split-screen down. If we do it right, we'll be able to see a timer, part of the history of moves, and part of the chat conversation at the same time as the board. That's the goal anyway.

The Chess Pieces

I don't care for the visual appearance of most C64 chess games. Chess Master 2000 is one of the best, in my opinion. It's multicolor, to get the most colors per piece, but it sacrifices a lot of resolution. Sargon is another popular version of chess for C64, which is a bit more representative of how many of them look. I think what bugs me is that they often make the pieces all approximately the same size. In a real chess game the pieces vary in size. The King and Queen should be the biggest. The pawns should be the smallest.

Chess Master 2000 (Screenshot) Sargon II (Screenshot)
Chess Master 2000 on the left. Sargon II on the right.

Here's what a nice set of wood pieces look like.

Wooden Chess Pieces (6-Piece Set)

So, here's what I came up with.

C64 OS Chess Pieces (6-Piece Set)
Looking at them like this, I now think the rook should be smaller.

Now, about the board colors. It looks to me like I had the same idea as the creators of Sargon. In hires, you can only have two 2 colors per cell. So, you're going to have some white pieces on white squares. And you're going to have some black pieces on black squares. Rather than trying to outline the pieces, it is easiest to use some other set of light and dark colors for the squares. The Sargon screenshot shows blue and light blue. I chose brown and light red, but it's the same idea. When your white piece is on a light square, the cells are foreground white, background light red. When a black piece is on a dark square, it's foreground black and background brown. All the pieces stand out nicely, and each piece gets to have a crisp singular shape.

The Background

It seems that many chess games do actually opt to just use all the available space on the screen. Because I wanted to have room vertically to position the splitter I made my board only 2/3rds the size of many others. Once I did this though, there is empty space around the board. So, I opted to fill up that space with a logo and what I think looks like a pretty cool background.

The logo was converted from a font on a Mac, and then touched up, and I added the color to make it look like it matches the brown and light red of the board. The background came from an image of some chess pieces that I scaled down and put through a black and white dithering algorithm. I touched it up by hand too, to get better contrast. I feel it adds a touch of graphical pleasantness not typically found in a C64 chess game. Most are much more utilitarian. I haven't bothered to take the next step, but one obvious option is to draw or convert (or even commission) artwork for the background. The app could easily have multiple backgrounds stored in its app bundle. You could select from a set of built in options, or it could randomly cycle through the available options. This starts to feel like a feature you'd only find on more modern computers.

Or, more modern still, if you opened the C64 OS Files utility, you could navigate the file system, find your own image (any hires bitmap would do), choose to "open" it, and the program could use your custom image. Most of the time a C64 program wouldn't bother to implement this sort of feature. You certainly wouldn't bother to embed a whole file navigation program within your chess program. Nor typically would it support sourcing the custom resource from another partition, subdirectory or even another device. But a perk of being inside an operating system is that such a feature is virtually free. You only need to support one tiny little message command. And maybe a couple of lines to write the serialized file reference to a custom-background settings file in the app bundle, so it would load in the next time you open the app. That's very cool. The opportunities an OS presents really make me happy.

C64 OS Chess fullscreen graphics mode
This image shows black and bottom, white at top. I've since reversed these.

The Gameplay and Game Logic

Although it would be cool to have a 1-Player mode vs. the computer, A) I'm not that good of a programmer. I doubt I could write even a crappy chess AI in 6502 assembly. B) It's a bit out of the scope of the project. I don't want to spend more than a couple of weeks of actual programming time working on this game. Because there are many other priorities.

Instead, I've opted to make it a 2-Player game only, and focus on getting the networking abstraction layer up and running. Designing that mechanism and writing that code might sound like I'm doing a bunch of work on a chess program, but in fact it's code that will be reusable by hopefully many games and applications yet to be written. On top of whatever that networking layer ends up looking like, I'll do the chat protocol and turn-based game state transfer stuff which could turn into a nice little library. That library could then be embedded into numerous other turn-based games that could substantially shorten their development time.

A few quick ideas have already come to mind. Battleship. It's absolutely ideal for a 2-Player, turn-based network library with support for chat. Chat with your friend, sip a coffee, lay out your ships and enjoy an evening of fun and camaraderie, facilitated by your beloved Commodore 64 (and that new WiFi modem you bought so you could frig around on BBSes again.) It's almost more ideal than chess, as chess is a game of perfect information (everyone gets to see both player's pieces), whereas in Battleship you only see your own pieces. Scrabble would also be a good fit (it's on a 15x15 board.) Some other games similar to chess would also work, like checkers or go, or backgammon.

A mock up of a possible Scrabble clone.
I couldn't help myself. Here's a quick mock up of what Scrabble could look like.

I transcribed the bitmaps for all of the chess pieces into the game. And configured virtual boards as arrays in memory for the standard reset position for the pieces, plus the current state of the game. A draw routine copies the bitmapped pieces onto the board and sets the correct foreground/background color combination for whose piece is on each square.

Next I did hit detection, which was very simple from the C64 OS mouse events. When the user clicks a valid piece, (a piece that belongs to the player whose turn it is,) a routine calculates all of the valid moves that can be made by that piece. Any square along a path of movement gets flagged in the virtual board as a valid space to be moved to. Any friendly piece encountered along the path of movement terminates that path of movement, as does encountering the edge of the board. Any enemy piece encountered along a path of movement also terminates that path, but it flags that enemy location as a valid place to move and capture.

When the user has clicked one of his pieces and all of the valid moves are calculated the board colors are redrawn. Valid move-to squares get highlighted in green (the colors may change, or may end up being customizable, or turn-off-able), and any valid move-to square occupied by an enemy piece gets colored in red, to indicate the piece is capturable. The user can then click any other square on the board. If he clicks an empty square that isn't flagged as a valid place the piece can move to, a standard system alert is triggered (border blinks) and no state is changed. If he clicks a valid unoccupied square, his piece is moved, the board graphics are updated, the valid move squares are reset, and the turn changes. If the player clicked on a valid move square that was occupied by an enemy piece, that piece is replace on the board by the capturing piece. Very simple.

A few nice little touches. When one of your pieces is selected, if you click another one of your own pieces it changes the selection to that piece, highlighting it and recomputing and highlighting all the valid moves for that newly selected piece. If you click off the board, say onto the background, any selected piece is unselected. All of this only took about one weekend to implement! So it was really fast. And I killed a bunch of time on one more nicety that wasn't necessary but feels great. You can drag and drop your piece. Mouse down on the piece and it becomes selected, and its valid moves get computed and highlighted. Drag your mouse and the piece follows the mouse around the board. If you drop on an invalid square, you get the standard system alert (border blink), and it reverts to nothing being selected or highlighted. If you drop on a valid square it moves your piece there. So, interacting with the board with the mouse feels absolutely zippy and comfortable. Not to mention, your mouse cursor and cursor colors, and your mouse preferences like acceleration all derive from the OS settings.

Valid moves highlighted for the Queen.
Queen's highlighted in blue. Moves highlighted in green/lt.green. Capturable pieces in red.

The next step, which I've worked out, but haven't implemented is detection of being in check. If you're in check, and you make a move, and you are still in check the move will be rejected. If you're not in check, but you make a move and you end up in check, such a move will also be rejected. Many of the other rules of chess, such as how long each player is allowed to spend on a turn, how many times the game is allowed to repeat the same state, if you're in a no-win end-scenario, etc., I will likely never code explicitly. It will be left to the players to see and negotiate with each other over the chat. So the game is very close to completion as far as the mechanics are concerned. I still have to implement en passant and castling. (Less common, but important manoeuvres.) And possibly validating stalemate.

Here's the kicker. All of that, so far, is less than 2 kilobytes of code. And took only one weekend to write. I feel pretty good about that! Now, it's far from complete, but the next stages of development become fuzzy as to exactly what I'm working on... Chess? Or a turn-based network gaming library? I prefer to count it towards the latter. After all, such code will be usable in Scrabble and Battleship, and maybe even some social networking or other kinds of apps.

So, let's talk about networking.

Networking

When I returned to the Commodore scene in 2016, the first thing I bought was a 64NIC+ ethernet cartridge. I got a couple of those even before I got an SD2IEC device. Turns out that was a mistake actually. For some reason I was really eager to write networking code. But I jumped the gun by about 2 years. There was a lot of stuff that had to be written first.

In the interim I became aware of the proliferation of WiFi modems. And I also got hold of both a 1541 Ultimate II+ and an Ultimate 64. Both of which have an ethernet port (and the Ulimate 64 has a WiFi module), with a simplified API that abstracts a bunch of the networking overhead. While I plan to return to the 64NIC+ (and RR-NET) in the future, both of these alternatives are much lower-hanging fruit.

I'm going to leave the 1541 Ultimate and Ultimate 64 to one side, and return to them in the future. Today I'm going to focus on the WiFi modems, but also on dial-up modems and null modems.

RS-232 and Standard Protocol

Let's start with RS-232. RS-232 is a standard for serial communications. But frankly, it's kinda finicky.

First, consider how a simple serial protocol works, such as, for example, how a computer (a Nintendo even) reads the set of buttons in from a NES controller. There are only 5 lines, but there are 8 buttons. On a SNES controller there are similarly few lines and twice as many, 16, buttons. Clearly, the data is being transferred by a serial protocol. But this protocol is very easy to understand. The five lines are: +5VDC, GND, DATA, CLOCK and MODE. The +5 and GND power an 8-bit shift register IC. When the MODE line is activated the shift register captures the state of the 8 buttons as 8 bits into one byte of data that it stores temporarily. The first bit is automatically available on the DATA line. The computer can read that line at its leisure. It can hold in that state indefinitely, or it can read it immediately. As soon as the computer is done reading the DATA line, it can raise and lower the CLOCK line. This signal causes the shift register to shift the data by one bit, putting the next bit on the DATA line. The computer can now read the DATA line again. But once again, it's in no hurry to do so, as the bit will remain on the DATA line until the computer sends another clock pulse.

This process is repeated 8 times to read in the bit status of the 8 buttons. If, like me, you spent your childhood wondering about how this works, it's really quite clever. This kind of serial is called synchronous. The computer sets the pace of the transmission of data. The computer can tick through the bits as slowly or as quickly as it wants, as long as it doesn't send clock pulses faster than the controller's shift register IC can handle. It really is very simple. Send a clock pulse, and a new bit will be waiting on the data line. That's it.

(If you're interested, you can read about the NES controller in much more detail in my earlier post NES to C64 Controller Mod.)

RS-232 is technically just an electrical specification, but in practice it is usually used with the UART (Universal Asynchronous Receiver/Transmitter) protocol.1 RS-232 is used as a standard way to transfer data between two devices. But how the data actually gets transferred is much more complicated than the simple synchronous NES controller example. In the minimum implementation only three wires are required for full duplex (simultaneous, 2-way) communication. GND, TX and RX. Ground, Transmit and Receive. The transmit line of one device gets connected to the receive line on the other device and vice versa. The computer can now send data via its transmit line to the device's receiver, and the device can transmit data via its transmit line to the computer's receiver. That sounds easy enough right?

But, wait. What about the clock signal line? Without a clocking signal, how does the device know when the computer has read the data? How does it know when to send the next bit? In other words, without a synchronizing signal line, how do they in fact stay in sync?

This is where it gets complicated and finicky. The protocol sends each data word wrapped by signaling bits sent on the same line as the data. The start of a word is signaled with a "start bit." The start bit in fact is just the transmitting device pulling its transmit line low. As soon as the receiving end gets that signal it knows that one data word will be transmitted immediately following. But then, it gets more complicated. The transmitting end starts sending the bits at a given rate, raising and lowering its transmit line according to the ON/OFF value of the bits. The transmitter holds the line in the state for each bit for a fixed amount of time. Meanwhile the receiving device, knowing that data is on its way, samples its receive line at a fixed interval, interpreting each sampled value as the state of the next bit.

There are some advantages to this scheme:

  1. You need fewer wires.
  2. Transmission can go in both directions.
  3. The transmitting device decides when to send another word of data, rather than the receiver polling the transmitter for another byte. (Unlike how the computer polls the NES controller.)

Well that's cool. What's so finicky about that? All the parameters of the transmission protocol can vary, and they have to be agreed upon by both sides or communications can't work. AND, there is no standardized way for the devices to negociate those parameters, they have to be set manually. So what are these parameters?

  • Number of bits in a data word. (7 or 8 bits)
  • Parity bit (Add in a simple error correction bit at the end of the data word)
  • Number of stop bits (1 or 2 end signals)
  • Transmission rate (Time spent transmitting each bit)

All of these parameters must be agreed upon, or communications will fail. While they are all obviously important, the transmission rate feels like it's of particular importance. The sender spends a fixed amount of time holding the transmit line either high or low, and the receiver samples it… at the same rate. If the sender is changing the state of the line 100 times a second and the receiver is only sampling at 80 times a second, it won't take long before the two are misaligned and the receiver is reading garbage. At relatively low speeds, the precision of the timing between the transmitter and sender is quite relaxed. Even if they're out by a few percentage points, there is no problem. They only have to stay aligned (by timing) for one frame, or around 10 bits. After that, they get realigned by the start bit signal of the next frame.

Typically, an RS-232 device will come with a user's manual that says what its communications parameters are. A baud rate (bits per second), say, 1200, and a shorthand like 8N1, for 8-bit data word, no parity bit, and 1 stop bit. Then you manually configure the serial port on your computer to match.

(If you're interested, you can read about RS-232 and UART in much more detail in this informative article UART Communication Protocol - How it works?.)

C64 User Port and Built-in RS-232

Now let's talk about the Commodore 64. The C64's KERNAL has built-in support for RS-232 communications over the user port.


NOTE: Real RS-232 uses voltage levels from -15 to +15 volts. The user port, driven directly by CIA2's Port B, uses TTL levels, 0 to +5 volts. A TTL to RS-232 adapter is needed to interface with devices that don't also support TTL voltage levels. This is noted in the C64 Programmer's Reference Guide.

Programmer's Reference Guide talks about translating from TTL levels to RS-232 standard voltage levels.
You'll see shortly an inexpensive way to adapt the voltage levels from TTL to the standard.

RS-232 is in fact a first-class citizen in terms of devices the KERNAL recognizes.

Device I/O Device Number Secondary Address
Keyboard Input 0
Datasette Both 1 0, 1, 2
RS232 Both 2
Screen Both 3
Printers Output 4, 5 0, 7
Plotters Output 6, 7
Disk Drives Both 8... 30 0, 1, 2... 14, 15

Isn't that fantastic? You can turn on your C64 and type this:

OPEN2,2,0,chr$(8)

And you have just opened a full duplex RS-232 connection, at 1200 baud, 8N1, to whatever happens to be connected to the user port. And you can subsequently send that device some data by typing this:

PRINT#2,"HI THERE! I'M A C64 TALKING RS-232!"

I mean, that is just very cool. Right out of the box, from direct mode, you don't even need a program, you can read and write data to another device over RS-232. It's just so cool, if only we had an RS-232 device to try communicating with. But where can we find one of those? Well that's easy, how about another Commodore 64??! Naturally.

Just to play around and see how easy this is (or to find out if it's not so easy), I made two of these handy little user port breakout boards. One for each computer. The edge connectors were ones I had kicking around in a box of odds and ends. The PCB prototype boards I purchased from Shareware Plus and are listed in the Buyer's Guide. The pin headers, I had extras from other projects.

User port breakout board #1. User port breakout board #2.

I labeled the edge connector to remember which side is numbers and which letters. The pins of the pin header face up when connecting the board to the C64. And, as you can see it is very easy to connect arduino-style wires to these terminals. I can use these boards for other things in the future too, as the user port isn't just limited to doing RS-232.

Now, technically, because two C64s are both using TTL voltage levels, and we're connecting them over a short distance, we could just connect the pins of one breakout board directly to the pins of the other breakout board and that should work. However, I found this article on biorhythm.com, DIY RS-232 Interface for Commodore C64 for under $15, about how to make (voltage-level-correcting) standard RS-232 adapter cables for the C64. Many months back, I picked up a couple of these tiny adapters for only a few dollars each.

RS-232 to TTL Module.

Initially, I wired these up, but I didn't quite understand which lines were required. The article said to wire one pin to two pins, and I just picked one instead of connecting to both. And when I tried them, they didn't work.

Here's how they looked when I wired them up missing some of the connections. These look really cool, even though they don't work.

TTL adapters connected to user port breakout boards. TTL adapters connected to user port breakout boards.

But the fact that they weren't working got me to investigate a bit closer how they are supposed to work. I looked into the C64's schematics, and into the KERNAL source code. Such exploration is an absolute pleasure.

Going all the way back to 1971, the UART protocol has been integrated into single chips. This work on integrated circuits actually predates the development of microprocessors themselves. The first integrated UART circuit was developed by DEC for the PDP-1 that, by that time, was already 10 years old. So, UART chips existed. The IBM PC, introduced six months before the Commodore 64, included such a chip, the 8250. But, Commodore being Commodore, they wanted to keep the price low. So the C64 does not include a hardware UART. And this wasn't necessarily a terrible idea. Millions of C64 users paid less for their computer, yet never missed having this feature.

Instead, the C64's KERNAL includes a software implementation of MOS's own UART chip, the 6551. The KERNAL documentation even refers to C64 workspace memory locations as though they are 6551 registers. So let's see how this works.

Schematic showing CIA2's connections to the user port and CPU.
Much detail has been removed for clarity of focus.

We can see in this clip of the C64 schematic that the bulk of the pins on the user port are wired directly to CIA 2, 14 of 24 total. 2 more lines are connected to CIA 1. Plus 1 reset line, and the rest are all either power or ground. So, CIA 2 is really what the user port is all about.

According to the article on biorhythm.com, the TTL-RS232 module should be connected to the user port like this:

RS232-TTL Module C64 User Port
GND A & N
TXD M
RXD B & C
VCC 2

If we take a look at the schematic, VCC goes to pin 2, which is just +5VDC. And GND goes to both A and N. Sure enough, both A and N are just GND on the user port. TXD, the transmit line goes to pin M. This comes from CIA 2's Port A, bit 2. Interesting, but nothing special. The software will use that port bit to output data during RS-232 writes.

The interesting part is that RXD, the computer's RS-232 receive line, is connected to both pins B and C. C goes over to CIA 2's Port B, bit 0. That's the CIA pin that will be used for reading data from RS-232. But, the read line also connects to user port pin B. And that, as we can see in the schematic, is the CIA's /FLAG line. Remember, the slash means it's low active. And there is a pull up resistor hanging off it, so /FLAG will be held inactive as long as nothing is connected to the user port.

The /FLAG pin on the CIA triggers an interrupt. The CIA can mask its own interrupt sources, but when unmasked this will cause the CIA's own /IRQ line to be activated. And this, in turn, is connected to the CPU's /NMI line. We read earlier that the way RS-232 signals the beginning of a frame of data is with a "start bit." The start bit is the mere act of the transmitting device pulling its transmit line low. Since the device's transmit line connects to the C64's receive line, the start bit signal pulls the /FLAG line on the CIA low. This triggers an NMI in the CPU which with a couple of quick checks determines the source to be the start of an RS-232 receive event on the CIA.

Damn, that is really cool. It's so tightly integrated. I mean, the start bit signal of RS-232 pulling low, and the /FLAG line on the CIA being low-active, this is not a coincidence. These protocols and chips were designed to work together.

Timing

Now we get down to the harsh realities of the C64's built-in RS-232 support. And what it means for the protocol to be implemented in software.

When device 2 is opened by BASIC, the call is passed through to the KERNAL routines. Two memory pages of space are made available at the top of BASIC memory2 and pointers to these two pages are configured in workspace memory as the RS-232 incoming and outgoing buffers. At the same time, the selected baud rate is used to lookup timing values from a table. With each doubling of the supported baud rate, from 300 to 600 to 1200, the timing value gets approximately halved.

Count Baud Rate
1605 300 baud
752 600 baud
3263 1200 baud
Above numbers based on the NTSC C64 clock rate.

Here's the thing. After the start bit triggers the NMI routine to begin reading in an RS-232 frame, all the subsequent bits being read will be coming into Port B's bit 1. But, they will also, if low, be triggering /FLAG, because the RXD line is hardwired to /FLAG, after all. What the routine does to handle this is, upon the first NMI trigger for a new data frame, the CIA's interrupt mask is changed to ignore /FLAG but to listen to its own internal timers. Then the count value for the current baud rate gets copied into one of the CIA's timers and it starts counting down on every clock cycle.

On the first interrupt, starting a data frame, the routine prepares some variables, such as setting a count for how many bits should come in this frame. (length of data word + parity bit + number of stop bits.) Then the routine returns, and the CPU continues servicing the rest of the computer.

When the CIA's timer hits zero, an NMI is triggered again. This time the routine knows its in the middle of receiving a frame. It samples the input Port line, shifts it onto the incoming byte and decrements the variable for the number of bits yet to come. It resets the CIA timer and returns.

The CIA's timer causes the routine to wake back up just in time to sample the incoming data line to get one more bit. Until, eventually, all the bits for this frame have been read in. If the parity bit is being used parity will be computed, and this could result in a parity error flag being set on the RS-232 status byte. (This status byte is probably one of those emulated 6551 registers.) The final byte, now that it's had all of its bits read in, gets written to the receive buffer in memory.

After receiving the complete frame, the CIA timers are not started again. Instead, the interrupt mask register is reconfigured to pay attention to the /FLAG line again. So let's think about this for a second. The rate of transfer, the baud rate, is a measure of how quickly individual bits are transferred from the device to the computer, within a single frame. It is not a measure of how quickly, or how frequently, new words of data will become available to transfer. That just makes sense, right? If the sending device wants to send 5 bytes, it will send those 5 bytes one right after the next. They can't arrive faster than the baud rate, because it is a serial transmission, and each byte has to finish being sent before the next one can start being sent. However, the receiving device has no idea how much data is coming. After each frame, an arbitrary amount of time could pass before another byte is available.

Each byte that is transferred has to be wrapped (or "framed") in its own start bit and stop bit(s) wrapper. After every frame, the computer is prepared to wait indefinitely for another frame to begin.

Meanwhile, your program is never explicitly notified that there is new data in the RS-232 incoming buffer. If you have opened an RS-232 connection, and you care not to lose any data that may be arriving from the other end of that connection, it is your responsibility to continuously poll the incoming buffer to see if there is anything in it. This is actually not too dissimilar to polling the keyboard buffer for new key presses, and in C64 OS, you also poll the mouse queue for new mouse events.

Just as with the keyboard buffer, which is only 10-bytes big, if you don't continously remove bytes from the buffer, the buffer will fill up and some key presses will be lost forever. If you don't poll, discover and retrieve bytes out of the RS-232 buffer fast enough, and more data continues to pile in, the buffer (256-bytes big for RS-232) will eventually overflow and data arriving at the connection will be lost. When the NMI receive routine is finished getting a whole byte, it tries to copy it into the buffer. But if it finds the buffer full it begins overwriting old data, and sets a buffer overrun flag in the RS-232 status byte.

The KERNAL supports baud rates up to 1200. Which is enough to receive about 150 characters per second (or a bit less). But, believe it or not, this is too fast for BASIC to handle. BASIC is just that slow. If you attempt to read from the buffer and print the data to the screen, even using as tight a loop as BASIC can muster, like this:

20 GET#2,A$: IF A$="" THEN 20
30 PRINT A$;: GOTO 20

If it is sent a continuous stream of data, you will certainly get corrupted output as the buffer will soon overflow.

Here's how the breakout board looks when the TTL-RS232 module is actually wired up to it correctly:

The TTL-RS232 module wired up correctly to the breakout board.

That was a lot of discussion. But, let's see now the result of my attempt to connect one C64 directly to another. (Actually, to a C128 in this case.)

So that's pretty cool! It works. And on the C64c side of things there was really nothing more than a couple of lines of BASIC. The C128 side was running just a few lines of assembly. A) I get to test out how to work with RS-232 in assemby (including choosing my own memory pages for input and output buffers), and B) it demonstrates that the computer itself can pull in and print out data fast enough to not have a buffer overrun, even if BASIC cannot.

By the way, it turns out Simulant is selling these C64/128 user port to RS-232 adapters, brand new, with a nice little 3D printed case. I just discovered them, and I'll be adding these to the Buyer's Guide in short order.

User port RS-232 adapter. User port RS-232 adapter.

Dial-up Modems and UART Cartridges

What we just looked at is called a null modem. It is just an ordinary RS-232 serial cable, strung between the RS-232 serial ports of two computers, with a null modem adapter shoved in between. The null modem simply crosses the transmit line of one side over to the receive line on the other side, and vice versa.

A null modem is a simple form of networking. It can be used as a peer to peer, C64 to C64, form of networking. Alone it's not going to get you on the internet, but it can still be pretty cool. Chess is a game of perfect information, so there doesn't seem to be much downside to two players using the same computer. But for Battleship, you don't want the other player to see where you've positioned your ships. I can easily see two Commodore 64s, back to back with the players facing each other and seeing only their own screen. Sounds like good party fun. Scrabble would also benefit from private screens, as you don't want the other player to see your bench full of letters.

Two original GAME BOYs connected together by cable.
Remember this? Null modem between two C64s is a lot like this.

When you look at the image above,4 it just seems obvious that any abstract networking layer for C64 OS, especially if used by two player games, should support a simple null modem connection.

Well, you know, real modems, dial-up modems, aren't all that different than null modems. Standard Commodore modems, like the 1670, connect to the computer via the user port. They exchange information with the computer over the RS-232/UART protocol.

The main difference is that the data sent over RS-232 is received by the modem device. A series of commands, (Hayes commands,) can be used to configure the settings of the device and, most importantly, to tell the device to make a connection to another modem by dialing out with a telephone number. After the modem connects to the remote modem, the device switches from a command mode into a data mode. In data mode, everything that comes across RS-232 from the computer is treated as data and is automatically transmitted to the other side. Incoming data is similarly passed through directly to the computer over RS-232.

Commodore 1670. 1200 baud user port modem.

Besides the thin command layer to allow the computer to configure the modem and tell it where to dial out to, once connected, the modem itself becomes nearly invisible to the computer. The modem's streaming data mode behaves identically to a simple null modem. Any character sent by the computer gets received as a raw character by the other computer. The only exception is a special escape sequence (1-second pause +++ 1-second pause) that the modem sees and interprets as the signal to switch back to command mode.

In short, if you're going to support a null modem connection for 2-player games and chat, you basically are supporting dial-up modems too. Because the only thing you need to do is send an initial simple hayes command over the RS-232 connection, which the modem will interpret as a command to make a remote connection, and then switch into streaming mode. Boom, now its behavior is identical to a null modem. Easy, right? All tied up.

But just as we think we've got this low-end, simple and classic networking technology licked, we realize we've got a serious problem. And it's going to come back to bite us with some more modern networking solutions too. While the KERNAL supports RS-232, the lack of a dedicated hardware UART puts a brutal and unforgiving burden on the CPU. The transfer of each and every bit of data over the RS-232 connection requires a CPU interrupt. Plus, there is a minimum of 2 additional bits per byte, leading to a walloping 20% overhead to the payload.

You might say, yeah, well, what's the big deal? So you get a little less CPU horse power for your game engine, and only during the brief periods of time when data actually needs to be transmitted. The problem comes when you want to use the user port's RS-232 in conjunction with splitscreen. The stability of splitscreen, as I'll go into detail in a future post, depends on extremely precise timing, down to a tiny window of about 12 to 16 CPU cycles. If the routine that is responsible for changing the VIC-II's video mode doesn't run at precisely the correct moment, the window gets missed, and glaring visual instabilities wreak havoc where the splitscreen division has been set.

But, the CPU is reponsible for reading the data in from the user port. And it too depends on extremely precise timing. If the CPU were delayed by just a handful of cycles, the other side would have already set the next bit, and the previous bit would be lost forever. The incoming data could easily be corrupted if the timing isn't precise. And so the problem is that both splitscreen and RS-232 over the user port depend on precise timing, and they both require the CPU to fulfill their needs. The CPU is thus under contention and only one of these two can win.

As it happens, the user port is connected to CIA 2, and CIA 2's /IRQ line is connected to the CPU's /NMI line. The NMI is not maskable, which means it's going to win. Incoming data from RS-232 can interrupt at any time. It can even interrupt the current IRQ's interrupt service routine. So the routine destined to change the VIC-II's mode could be 10 cycles before the precious tiny window, and BOOM, in comes some RS-232 data. The NMI sucks CPU control away from its carefully timed work. By the time the NMI has finished processing the incoming RS-232 bit and returned, the VIC-II's raster beam is already way beyond the edge of the split. And it's been drawing crap (hires bitmap mode's color data) in text mode for the last few raster lines.

The way I see it, there are only a couple of ways out of this.

1) Don't use splitscreen with programs that use user port RS-232.

Rather than leaving the program in splitscreen mode, explicitly switch back and forth between fullscreen text mode UI and fullscreen graphics mode. It's not perfect, but it relieves the CPU of the burden of having to switch video modes with cycle perfect timing.

2) Suffer the occational visual glitches.

You could just suck it up and suffer through seeing the division of the splitscreen occasionally flicker and then go back to normal. After all, it will only flicker if data happens to be coming in, and its timing happens to coincide with the raster interrupt. Depending on how light weight the game's data exchange is, it might be a problem that occurs sufficiently infrequently that a few flickering raster split instabilities don't bother you.

3) Last, but not least... don't use the user port's RS-232!

Commodore may have been cheap and decided not to include a hardware UART on the C64's mainboard, but that doesn't mean C64's can't use them, and it doesn't mean you can't add one on.

There were lots of CMD Swiftlink and Turbo232 cartridges sold throughout the 1990s and early 2000s. But better yet, a clone of the Turbo232 made by Gabriele Gorla of GGLabs called the GLINK232 is commercially available today. It's listed in the Buyer's Guide in the networking section. And is available for purchase, today, on Ebay or through the GGLabs website.

CMD Turbo232 High Speed RS-232 Cartridge. GGLabs' GLINK232 Turbo232 Cartridge Clone.

You know now a bit about how RS-232 works, and how the KERNAL implements support with the 6510 and a CIA chip. But it's slow, and it's CPU heavy, and it interferes with the timing of other CPU dependent operations. A hardware UART solves all of these problems.

The UART chip in these cartridges has its own clocking signal, and does all of its own timing. It detects the start bit, it samples its own port lines entirely independently from anything the CPU is doing. When it gets a full byte it stashes that byte into a 1-byte, on-cartridge buffer. It fires an interrupt (configurable on the Swiftlink and GLINK232 as either an IRQ or an NMI, depending on your needs) and immediately becomes ready to start receiving the next byte.

Upon servicing the IRQ, reading the byte into the C64 can be done with a single instruction by loading the entire byte from a register in the cartridge. It is so much more efficient to use a hardware UART that these cartridges support up to 230Kbps! That's 4X faster than a 56K modem. But what's even better, it won't interfere with the raster interrupt timing necessary for split screen.

The downside, a small downside, is that the C64's KERNAL doesn't have native support for doing RS-232 via one of these cartridges. So you can't simply flip the machine on and use the OPEN, PRINT# and GET# commands in BASIC to start sending and receiving RS-232 data. But they're quite easy to program from assembly. And writing your own fetch routine gives you greater control over buffering the incoming data. Support for high speed serial cartridges can be used for null modem connections or with dial-up modems.

But. There's always a but. A null modem connection would be fun at a party, it won't connect you with other Commodore users on the internet. And so, are we really left with just the option of using a dial-up modem over one of these fancy high speed serial cartridges? Doesn't that seem kinda, you know, 20 years out of date?

PEW research showing decline of dial-up.

Dial-up peaked around 2001, with 40% of American adults using it to access the internet. By 2013 dial-up had fallen to just 3% and that was 6 years ago. Today, you'd be lucky to find somewhere to dial to. But of course, dial-up modems should remain an option. Even if it's just for one C64 user to dial another.

WiFi Modems

Finally we return to WiFi Modems. I only became aware of WiFi modems when I got a demo by Jay Hamill of several kinds he had with him at World of Commodore 2017. These modems sprang up when a cheap RS-232 WiFi module, with a built-in TCP/IP stack, hit the market for reasons that had nothing to do with nerd hobbies classic computer users.

But they very quickly became the centerpiece of a new generation of networking abilities for retro computer enthusiasts. They behave, to the computer, much like a dial-up modem does. The computer communicates with the modem over RS-232, and sends it simple commands to join modern WiFi hotspots and make TCP/IP connections to the outside world. All of the heavy lifting is in the device, relieving the C64 (and the 6502 and its precious 64 kilobytes of memory) from having to implement all of the gratuitous details of modern networking protocols.

Far from being a hit to our pride—What, your little old 8-bit computer can't handle implementing a TCP/IP stack without help from the outside??—in fact this is exactly how Commodore 64's were designed to work. Look no further than the 1541 disk drive itself. 5 Commodore DOS runs entirely inside that stripped down 6502-based computer-in-a-box that is connected to the main computer with an IEC cable. The C64 was engineered to be the hub at the center of a range of connected intelligent peripherals. SD2IEC being a recent brilliant example.


Given what we've discussed about RS-232 and null and dial-up modems now we can apply this to WiFi modems. Let's take a look at a few of the most popular C64/128 WiFi modems out there.

WiModem. Wifi Modem (in a very professional case). C64 Net Wifi Modem.

Do you see something in common between each of these modems? That's right, they all have a 24 pin edge connector, because they all connect to the user port. Just a like the good old 1670 modem. No matter how cool these are, and they are very cool, as long as they connect via RS-232 over the user port, they are always going to suffer the same downsides of doing RS-232 without a hardware UART. It doesn't even matter what TCP/IP features these modems offer, if they exchange data with the C64 via RS-232 over the user port, they're going to be beating on the NMI ceaselessly.

That might not matter to some people. For people who just want to get on BBSes like they did back in their youth, these modems are amazing. And they're going to work with all the terminal software out there, because they connect using the most common original standard.

But they're also going to be limited in speed. The KERNAL's own implementation only supports up to 1200 baud. Custom drivers on the C64 can extract up to 9600 baud from the user port, but given the CPU's involvement, it seems likely that sustaining a transfer speed this high would completely occupy the CPU, and of course, exacerbate any other timing related problems. Further, for some strange and technical reason, the C128 (even in C64 mode!) can't reach the same speeds that the C64 can, even with custom drivers. Something about the 128's hardware support for burst mode interferes with how the driver works.

For C64 OS, I see the limitations of the user port as rather steep. Even setting aside whether you want to use splitscreen mode concurrently with your WiFi modem, 1200 baud is not fast. It works out to somewhat under 150 bytes per second. For the game state of Chess, Battleship or Scrabble, okay that's not too bad. Even for sending chat messages back and forth, it's perfectly acceptable. But think about what it means for downloading a single Koala image file. Assuming we pump an image URL through the image conversion proxy at http://services.c64os.com, we're downloading either a 9 or 10 kilobyte file.

10 * 1024 = 10,240 bytes
10240 / 150 = 68 seconds... or >1 minute!!

At such a low speed as 1200 baud, it would take over a minute to download just a single graphic file. And that's AFTER it's already been put through the conversion proxy. Let's not even bother to think about how we'd deal with images read raw from the web.

At 2400, which the C128 can do with a custom driver, you're still talking 34 seconds. It's slow, it's damn slow. And if there is anything counter to the mission of C64 OS, it's for things to feel slow. If on the other hand we could run a WiFi modem through a GLINK232 at, say, 115Kbps, that has a maximum transfer rate of nearly 14 kilobytes per second. At that rate, a 10K graphic image could be downloaded into memory and displayed in under 1 second. Yeah baby, that's what we're talkin' about!

Well good news. We C64 users are not the only retro computer users in the world who want to make use of WiFi modems. Jim Drew at cbmstuff.com, who makes and sells the WiModem for C64, pictured leftmost in the above three examples, also makes a WiModem232. It's technically a version of the WiModem meant for use on an Amiga. It comes with a DB25 serial port connector, and can be plugged directly in the back of an Amiga without even needing a cable. But, there is nothing special about how it works. It's just regular RS-232, but it supports from 300bps to 230Kbps. It's the perfect companion to a new GLINK232 cartridge or your old Turbo232. (It'll work with a Swiftlink cartridge too, but Swiftlink maxes out at 38,400 baud, and is a bit more greedy in its usage of I/O space.)

WiModem232. Highspeed RS-232 version of the WiModem for C64's user port.
The OLED screen is optional on both the WiModem and the WiModem232.

Maybe I should add this one to the 8-Bit Buyer's Guide too.

Not All WiFi Modems Are Made Alike

Above I showed images of three WiFi modems. But there are many available, I've tried to list them in the Buyer's Guide, but other varieties pop up in limited quantities on Ebay from time to time. Not all of them have distinct branded names, so (like SD2IEC implementations) it can sometimes be hard to distinguish them by name alone. The above three, from left to right, are: WiModem (by Jim Drew of CBMStuff.com), then the generically named WiFi Modem (from the people who made uCassette and Tapuino, sold by Shareware Plus and on Ebay), and lastly C64Net WiFi (by electronicsisfun.com and sold on Ebay.)

While in principle they do the same thing, in practice there are some pretty big differences. In principle each is a WiFi module, that can connect to a WiFi router, that communicates with the computer over RS-232, and takes hayes commands in similar fashion to a dial-up modem. Like dial-up modems, although there is a generally standard set of commands (and command syntax) there are also variations, extensions and omissions. The variation in commands and in features makes it harder to write a single piece of software to make use of them all. Let me give a brief example before getting to something more difficult.

All of the modems need some way of joining a WiFi hotspot. To do this they need to specify the SSID, which is a human-readable name that identifies network, and may optionally take a password. In addition to a command to join a network, it is handy to have a command that lists the networks so you know which ones are available. So here they are on WiModem and C64Net:

  WiModem C64Net
List SSIDs AT*N ATW
Join Network AT*SSIDname,passphrase ATW"name,passphrase"

Quite simply, the commands are different. Furthermore, the output of each one's command to list the SSIDs is not structured the same. They are interpretable well enough if you're looking at the raw output, but the difference means that to parse them programmatically they need to be individually supported. There are other differences. The C64Net can be configured to do automatic ASCII/PETSCII translation in command mode, the WiModem operates only in ASCII mode. The WiModem has problems handling SSIDs that have a "space" character in them. This is likely the reason why the C64Net command takes the parameters in quotes.

C64 OS needs to handle these differences. But ideally the more similar they are, the better. When it comes to PETSCII translation, for example, that's a special feature only of C64Net. But because C64Net can also be put in ASCII mode, it makes sense for C64 OS to always handle the PETSCII translation, and then deal with all WiFi modems in ASCII mode only.

What about the communication with the modem itself, are we going to be talking RS-232 over the user port or a UART cartridge in the expansion port? You don't want a detail like this to be handled at the level of the application. And you also certainly don't want your Chess program to be involved with what it takes to get a given WiFi modem connected to a hotspot.

The main goal of a networking abstraction layer is to expose a common programmatic interface to the application. The application just wants to open a communications channel, with an address for the destination device. Nothing more.

The networking layer should be responsible for using the correct hardware interface (user port, UART cartridge, or something else.) And it should be the responsibility of some other program, such as a C64 OS utility, common to the whole system, to handle getting a WiFi modem authenticated and connected to a wireless router.

Now, if we go into the weeds a bit, it probably makes sense to split the work across two drivers. One that handles the physical communications layer, one that handles the device command syntax. Why? Well, take the WiModem for example. WiModem for the C64/128 and WiModem232 have the same command syntax, but they connect to the computer over different physical interfaces. What you really want is for the physical layer to be abstracted (as thinly as possible) to handle the physical layer stuff like the baud rate, use of parity bit, and the registers and/or KERNAL routines to use. Then a command layer that handles higher level stuff like connecting to a hotspot or opening a network connection.

The general flow of information would go something like this: A chess program calls into the command layer to open a connection to an address. The command layer knows the command string and argument format, which it passes to the hardware layer. And the hardware layer talks to the hardware interface to communicate out to the physical device.

Simple stack diagram of program to command driver to hardware driver to hardware interface to device.

This is better. At the top we have a sort of theory of the possible layers, and at the bottom we have three configurations that could easily be in use in practice. Notice how in the first example column the hardware interface layer has a Turbo232 driver. But at the layer below that instead of a Turbo232 cartridge its a GLink232 Cartridge. That's because they're compatible with each other. Meanwhile, C64Net and WiFi Modem are different command sets, but they both use the user port's RS-232 to talk to different hardware devices. Another possible configuration not shown here is the WiModem command layer, stacked atop the UserPort RS232 hardware interface layer. Same command layer, different hardware interface layer to talk to different hardware device that shares the same firmware. There could just as easily be a null modem or dial-up modem command layer, that sits above the hardware interface layer. If you send a command to open a connection to the null modem driver, it could simply virtualize that, flag that a connection is now open, and nakedly pass the data through to the hardware interface driver.

It's better, but it's not perfect. The hardware layer can handle different baud rates, right? For example, a Swiftlink cartridge can handle up to 38,400 baud, a Turbo232/GLink232 can handle up to 230,000 baud, and the user port perhaps 1200 with a driver that uses the KERNAL and 2400 with another that reimplements the routines. For a device to use a higher baud rate, though, it has to be told to switch to a higher rate. But that command originates from the command layer, because every device implements its configurable speed with a different command. So, there is a bit of cross play going on that makes things not so clean.

The Big Difference, and Final Thoughts

Okay, I said earlier that beyond differences in command syntax there are even bigger differences to deal with.

A null modem cable connects you, hardwires you even, to one other computer. And information can be streamed back and forth between the two.

A dial-up modem connects you, via a configurable phone number, to one other computer. And information can be streamed back and forth between you and the single current peer.

But the internet doesn't work like that. The internet uses TCP/IP, which allows a computer to open muliple connections to one or more computers simultaneously. Each connection is through a socket, which is an ip-address:port-number pair. While the connections are open, data can be sent and received independently on each socket.

TCP/IP can be used over dial-up modems by using a serial transport protocol layer first, like PPP. You dial up to just one internet service provider, but then you both run PPP which is the raw data layer between the modems, that acts as a bridge to allow you to open TCP/IP socket connections to the rest of the world. But TCP/IP is complicated, and PPP is kinda complicated too. The whole point of the WiFi modem handling the TCP/IP for the C64 is so the C64 doesn't have to worry about any of that.

The question is, how much of TCP/IP's flexibility and functionality is made available to the C64 via a WiFi modem? The answer is that it depends on which WiFi modem you use. So truly, not all WiFi modems are made alike.

In my experience, the majority (all but one? I'm not sure yet.) have support for one single streaming connection. The command mimics dialing a remote computer over the phone system, but instead of a phone number you supply a domain and port number. Once that single TCP/IP connection is established, the WiFi modem enters streaming mode, just like a modem once did. All data you send across the RS-232 connection at that point is automatically sent across that socket connection. Whilst in that streaming mode, if any other connection tries to come in, it gets a "BUSY" message, just like what would have happened if your phone line were tied up.

This is ideal for BBSes. It's just like a modem! Who could ask for more? To hang up, you do just what you always used to do. Hit "+++" the hayes escape sequence to get back to command mode. Then you can issue an ATH to "hang up" and close the connection, or ATO to get back "online." In fact, most of the WiFi modems out there advertise themselves as a way to get on BBSes again.

But is that all they can do?


There is one WiFi Modem that stands out above the others:

C64Net from ElectronicsIsFun.com

This one is much more sophisticated than the others. The firmware and feature set were written by Bo Zimmerman, as Zimodem. C64Net can establish multiple listening sockets, and open multiple outgoing connections, all at the same time. Each connection is assigned an ID, and there are commands for sending streams of data interleaved by dividing them into packets of a small size and targeting the packets at specific connections by their ID.

When data comes in, the modem provides the computer with a packet header specifying the source connection ID, the length of data in the packet, and a CRC checksum. The headers on these packets are extremely lightweight, just three numbers separated by spaces inside a set of square brackets.

[ ID SIZE CRC ]

An example might be like this:

[ 22 104 85 ]

The above example header would then be followed by exactly 104 bytes of data coming in on that socket. This allows long streams of data to come in from multiple sources simultaneously, without interrupting your ability to issue commands, open new connections or send data out on existing connections.

C64 Net Wifi Modem.
Advanced firmware gives C64Net a decisive edge in functionality.

C64Net gives you access to very nearly a full TCP/IP stack. And yet, it is incredibly simple and lightweight for the C64 to use. It can also go into streaming mode, like the others, so it can be used in a simple terminal software package to connect to a BBS. But in that mode its power is laid aside in the name of backwards compatibility.

The C64Net, for now, is only available in a user port RS-232 model. The ultimate combination would be the C64Net's advanced firmware in a hardware device that can be connected to a GLINK232 at 230Kbps. I've made noises at its creator/s to try to encourage its development. So we'll see what comes about.

In the meantime, what it means for C64 OS and its network abstraction layer, is that there should be support for multiple streaming connections, open at the same time, and identified with a numeric ID. This is actually quite reminiscent of the KERNAL's logical file table. The abstract network layer should allow the application to attempt to open multiple connections, and expect to get a connection ID in return. When writing data to the network, the application should be required to specify the connection ID.

If the network's current driver is for a null modem, or a dial-up modem, or even for one of the many simple WiFi modems, these only support a single connection at a time. But they should still allow the application to request to open a connection, and should return an ID. Even if they only ever return an ID of 1. If the application tries to open a second connection, the driver for those devices that don't support that should return a standard error indicating that no more connections can be opened.

The idea is that, the network interface from the perspective of the application should be consistent, regardless of the hardware under the hood. When the application asks the network layer to open a connection, and it provides an "address", that address could be an ip and port combination, or it could be a telephone number, or it could be null. If the driver is for a WiFi modem and it gets a phone number, it should just return a standard error saying the remote connection could not be established. And if the driver for a dial-up modem gets an ip:port it should recognize that it's not a phone number and give the same error. Meanwhile, an address of "null" should work only when the driver is a null modem driver. But, at the end of the day, when the application asks the user to provide an "address" you should be able to type in "null" or "8881239999" or "services.c64os.com:6400" or "123.54.23.22:6400" and the application shouldn't care, as long as it gets back a connection ID from which to read a stream of data, and to which to write a stream of data.

We'll leave it there for now. I hope to have something to demo at World of Commodore 2019. As always, please leave thoughts and comments in the comments section below.


  1. Note the word asynchronous in the name of the protocol. []
  2. This has the rather nasty side effect of destroying all current BASIC variables, by the way. So, if you're going to open an RS-232 connection in your BASIC program, you have to open it either at the beginning, or at a point where you don't care that all the variables will be reset. []
  3. Apparently there is a bug in the timing value for 1200 baud. The number just isn't correct. And leads to frequent errors. It can easily be compensated for by changing the number in workspace memory after opening device 2. []
  4. This brings back a lot of nostalgia for me. []
  5. I've yet to write about how Commodore disk drives work, specifically, but you can get a good glimpse into the relationship between computer and disk drive from this post. []

Do you like what you see?

You've just read one of my high-quality, long-form, weblog posts, for free! First, thank you for your interest, it makes producing this content feel worthwhile. I love to hear your input and feedback in the forums below. And I do my best to answer every question.

I'm creating C64 OS and documenting my progress along the way, to give something to you and contribute to the Commodore community. Please consider purchasing one of the items I am currently offering or making a small donation, to help me continue to bring you updates, in-depth technical discussions and programming reference. Your generous support is greatly appreciated.

Greg Naçu — C64OS.com

Want to support my hard work? Here's how!