Shufflepuck Cafe for Apple II

35 years later, the great Shufflepuck Cafe game is, finally, available on the 8 bits Apple II line of computers.

Download:

Requirements:

  • An Apple ][ or more recent, with 64kB of RAM or more.
  • A mouse (and MouseCard on ][, ][+ and IIe, in any slot but slot 3).
  • Optionally, a serial card for two-player games.
  • A real Apple II – emulators don’t do the game justice1.
  • The game is visually more enjoyable on a monochrome screen. This is a design choice. (If you are using a color monitor in conjunction with an RGB card (EVE, Feline, Video-7 or Apple RGB card, the game will be in black-and-white as intended)

More information:

I have tried to be faithful to the original game, and the implementation is almost complete. The only missing things are the player pusher’s configuration, and the mid-table blocker. I have had great joy developing it, and I hope you will have as much pleasure playing it on your Apple II.

The only Shufflepuck Cafe with a two players mode!

Got a null-modem serial cable and two Apple II computers? Lucky you: you will be able to play against your friends and family. Just plug in your cable and click on the phone next to DC3, the robot bartender.

As we all know, every Apple II computer under the sun has a weird serial port format : DB25 on the II+ and IIe, DIN-5 on the IIc, Mini-DIN-8 on the IIgs/IIc+. You may already have null-modem cables with one end being the standard DB-9, for ADTPro for example. If that’s the case, just link them both with a mini null-modem DB-9 male to DB-9 male2, it’s like maths: 3 null-modems equal 1 null-modem and it will work.

A short teaser of the game, against one of the toughest opponent.

The start of a game against Biff

A game over serial

My sons beta-testing two players mode

The serial protocol

I’d like to quickly explain how the two computers talk to each other because I’m quite happy about my implementation.

As you may know, the Apple II is quite slow, and the interval separating two frames is 16.7ms, during which we first have to draw the sprites while racing the CRT beam. It takes ~12ms at worst to draw the three sprites (yes. The Apple II is slow.) so there is not a lot available to do the rest – the game logic, and the serial communication must happen in 4.7ms at most, which roughly translates to 4700 cycles.

There was no way to have the communication asynchronous using IRQs, as they are expensive (at least 200 cycles), so no buffering was possible either. So, the implementation takes advantage of the fact that the serial chip(s) in Apple II computer (the ACIA 6551 for the 8 bits ones, and the Zilog 8530 for the IIgs) are full-duplex capable.

At each frame, both computers exchange a single byte, in a non-blocking manner. It can either contain a flag that something happened, or just the player’s paddle coordinates, mirrored.

In the second case, which is most of the time, each computer mirrors the X and Y coordinates of their player’s paddle. This fits in one byte thanks to the ranges involved and a bit of rounding error: X can be 0-224 (mirrored to 224-0) and Y can be 154-191 (mirrored to 38-1). The byte is then built with X’s five high bits and three bits of Y shifted right three times): XXXXXYYY. The rounding error resulting is 7 pixels on the X axis and 6 pixels on the Y axis, which is acceptable: from the other side of the table, that’s barely visible.

This byte is pushed to the serial chip data register, and right after we check if a byte arrived. If not, on to the next frame! Otherwise, we unpack and update the other player’s paddle coordinates so that it’s redrawn at the new place.

If something happened – that is: if the player hit the puck, the computer of that player has to provide the new puck parameters to the other one: X, Y, delta X and delta Y. The hitter is the source of truth, otherwise rounding errors make the trajectory drift apart quite quickly.

In this case, the flag is set ($FF, or 11111111, a value that can’t happen when exchanging coordinates because 224 is 11100000), the flag is sent, but after sending it, the computer blocks until it gets the same flag as an answer.

At this point I know both computers are at known places in their loop and ready to chat more (Ackhually, this is called an I/O barrier). One status byte follows (‘H’ for hit), an answer is awaited, then the four bytes required are sent using the same technique: putting the value in the serial data register, and wait for an answer to be read.

The same flag-based I/O barrier happens when a player misses the puck and it crashes on their side of the table. That fact is sent over serial, even if the opponent’s game engine may have arrived at the same conclusion at the same time: if for some reason a desync happens, that fixes it.

And that’s all. The serial communication takes about 300 cycles per frame in general, and 600 when the puck is hit.

At first, the communication was synchronous for every message, but as both computers’ vertical blanking is never synchronized unless one is very lucky turning them on at the same nanosecond, this induced frame dropping, which was unsatisfying. The opponent’s paddle coordinates can, after all, be late one frame!


  1. Known emulator issues
    AppleWin mouse emulation makes the game almost unplayable.
    Virtual ][ mouse emulation makes the game almost unplayable.
    MAME apple2e emulation flickers awfully. Use apple2c emulation. Mouse emulation feels kind of wrong and the pointer leaving the window makes playing annoying. Use -nowindow.
    Apple2ts.com flickers, feels slowed down. ↩︎
  2. Example of a convenient, but easy to lose, DB-9 male-male null modem ↩︎