CONTENTS

Home
Updates
Software
Electronics
Music
Resume
Contact


YouTube
Twitter
GitHub
LinkedIn


Cloudtari

Cloud Based Atari 2600

Posted: July 25, 2021

Introduction

It seems like a lot of big companies are creating ways to play games "in the cloud". The games are in some way being rendering on big servers and the display is streamed to low power consoles. This concept, along with wanting an excuse to play around with Kubernetes, inspired me to create an Atari 2600 emulator that can stream the game in a similar fashion.

I planned to use a Turing Pi for the cloud system, but the Raspberry Pi CM3+ modules I ordered in March are still backordered. When I get them I have a second project almost ready to go to run on them. For this project I ended up using two Raspberry Pi 4's.

The emulator itself is complete enough to run Joe Davisson's Space Revenge game. It's an 8k game written in Java (Joe was the one who added 6502 and Atari 2600 support to Java Grinder) with no copyright issues, so I figured this was the ideal game to show off. There are some Atari 2600 features missing from the emulator so I think not many other cartridges will work, but that can fixed at a later time.

Below is a video demo of the system along with a link to the git repository and a long description of how the different components work.

Video

The video above shows the two Raspberry Pi 4's along with the screen of a desktop x86 PC running 3 Atari 2600 ROMs in 3 Firefox windows. Unfortunately, due to the lighting and such, what's not visible is the browser address bar which would have shown each running game has a different IP address and port (depending on which of the two Raspberry Pis in the cluster it's running on and a unique port).

The Atari logo and Space Revenge come from Java Grinder and the heart comes from an Atari 2600 tutorial.

YouTube: https://www.youtube.com/watch?v=4xJULDDAJs0

Related Projects @mikekohn.net

Java Grinder: Playstation 2 Java, Nintendo 64 Java, Sega Genesis Java, Amiga Java, Apple IIgs Java, TI99/4A Java, C64 Java, dsPIC Mandelbrots, Atari 2600 Java, Intellivision Java, chipKIT Java, Java Grinder, naken_asm

Source Code

https://github.com/mikeakohn/cloudtari

Details

The biggest part of the project is the emulator which simulates the real Atari 2600 hardware. It's not exactly feature complete, but it's enough to prove the point of being able to play Atari games in the cloud. I wrote the code as quickly as possible because I was more interested in implementing the Kubernetes part of the project.

Key features of the emulator (currently):

  • Output to local computer (SDL), text (for debugging), VNC (remote desktop), or http as a stream of GIF images.
  • Debug mode (break at location and step, and also ranged instruction CPU cycle counting).
  • Modular design so that possibly other 6502 based systems could be emulated (Atari 5200, Atari 7800, Nintendo NES, etc).
  • Cartridge ROM sizes: 2k, 4k, and 8k.

What's currently missing:

  • Sound.
  • Copy sprites 2 or 3 times in hardware.
  • Scaled sprites are untested.
  • Second joystick is implemented but has no interface.

What could be interesting is implementing the second joystick for a separate display so that two random people from around the world could play Atari games with with each other.

To make the game network played there were a number of options that could have been implemented:

  • VNC or RDP (remote desktops).
  • Stream video as MPEG (using libavcodec/ffmpeg) to web browser and use a web interface to transmit joystick presses.
  • Web based interface as a series of GIF images.
  • Web based interface drawing on an HTML canvas.

I decided at first to just do VNC in this case. It requires a VNC viewing program and there's no sound, but it kind of seemed like the quickest to implement. Also the web interface seemed like it could have slower latency on the joystick presses if Connection Keep-Alive doesn't keep alive. The streaming as MPEG seems like it's probably closer to what the big cloud based gaming systems use and would have used a lot less network bandwidth than VNC.

Speaking of bandwidth, with some temporary debug code in the VNC server part of the code counting the number of bytes of data transmitted, The Space Revenge logo moving down and up the screen 1 time lasts about 10 seconds. The number of bytes sent when there is a full screen is around 565,506,080 (539MB). When incremental updates are sent it drops to 20,606,076 bytes (20MB). When the game is playing there should be quite a bit less data since only the ships and the missiles are moving. This seemed a bit excessive so I decided to test what some compressed images could get. Given that the display is 480 x 384 x 4 bytes, the sizes of the display are:

  • 720k uncompressed.
  • 3.6k as a GIF.
  • 11.7k as a JPEG.
  • 2.4k as a PNG.

After seeing these numbers I decided to also implement it as a stream of GIFs to a web browser. Javascript is used to request a new GIF 30 times a second while keep track of keypresses to upload to the server in the same request.

Running The Emulator

The cartridge used to develop the code is Joe Davisson's Space Revenge which can be downloaded here: space_revenge.bin. To run the emulator in SDL mode, where a window will open on the local machine, the following command can be run:

./cloudtari space_revenge.bin sdl

To run it with VNC on port 5900 (the default VNC port):

./cloudtari space_revenge.bin vnc 5900

Then from the client system, to play the game:

vncviewer <ipaddress>

To run it with HTTP on port 8080:

./cloudtari space_revenge.bin http 8080

Some debuging features:

./cloudtari space_revenge.bin debug ./cloudtari space_revenge.bin break 0xff71 ./cloudtari space_revenge.bin step 0xff71 ./cloudtari space_revenge.bin timer 0xff71 0xff77

The debug mode will run the code while after every instruction dumping the current state of the 6502, chips, and RAM. The break mode will run like debug mode but stop when the CPU hits the provided address. The step mode works like break mode except allows the user to keep pressing enter to run one more instruction. The timer mode works like debug mode except when the PC hits the start address, CPU cycles will be added up until the PC hits the stop address. At that point the program will stop and print out many CPU cycles were executed.

To play the games the following keys control joysticks / switches:

  • Arrow keys: Move the joystick up, down, left, right
  • Spacebar: Joystick fire button.
  • TAB: Select.
  • Enter: Reset.
  • ESC: Quit.

The Emulator

I started the emulator by mostly rewriting an old partially written Atari emulator I did in Java a few years ago called Jatari. The emulator consists of a few modules: M6502, MemoryBus, TIA, RIOT, and Television. There are also some extra modules such as Nework to take care of all the socket code and GifCompressor that creates GIF images from the current Atari 2600 display. The GifCompressor module was a port (and cleanup) of libkohn_gif which I wrote back in 2006 or so.

M6502.cxx emulates the 6502 CPU of the Atari 2600 and is pretty much the center of emulation. A single function call to the M6502 will execute a single instruction and return the number of CPU cycles it would have taken on real hardware. Any memory read / writes are done by making calls to the MemoryBus module which routes the operation to either the TIA or RIOT and deals with the memory mirroring.

The TIA object emulates the Atari 2600 TIA graphic chip. It includes drawing the playfield, sprites (players, missiles, and ball), and video sync signaling. The chip itself runs 3 times faster than the CPU so for every CPU cycle used in the 6502, 3 ticks are applied to the TIA. The TIA keeps track of the x,y position of the video beam and draws the appropriate pixel to the location in an image buffer.

The RIOT object has a countdown timer, I/O switches including the joystick, and 128 bytes of RAM.

The Television object is an abstract class that is currently extended by TelevisionNULL (for no graphics display, good for debugging), TelevisionSDL (for displaying directly on the local display, which was also good for debugging before the more complex VNC module was done), TelevisionVNC, and TelevisionHttp. The Television class has a refresh() function which is called when TIA gets a VSYNC strobe. For most modules this will force the display to be drawn. The Television object can deal with 8 bit images (needed for faster processing of GIFs) and 32 bit images needed for SDL and VNC. The subclass of Television tells the TIA which buffer to write to.

For the VNC module, on a refresh() the code will look to see which scanlines have changed and will build up a "rectangle" list of the changes to send over the network to the VNC client. If too many lines have changed a full refresh will be performed. The handle_events() function will check for keyboard events on the network and relay them back to the main() function in cloudtari.cxx.

The TelevisionHttp object will open up an http server on a port. When a browser connects and requests "/" it will pass a web page that has some Javascript that will request a GIF image 30 times a second. It also detects keypresses and queues them up to pass back in the same request as a GET query string.

The Cloud

For the Kubernetes part, I set up two Raspberry Pi 4's with 64 bit Ubuntu and followed a tutorial to install microk8s Kubernetes on them. I created a docker image (a Dockerfile and Makefile for building it is in the repository in scripts/docker) that holds a compiled version of Cloudtari and 3 copyright-free cartridge files. It ended up that building Cloudtari under the host system creates a binary that doesn't work in the docker container (wrong library versions) so I created another docker container for building Cloudtari (scripts/docker_build in the repo). Still requires a git clone and removing the SDL module, but other than that it builds a working executable.

After creating the docker container I got it imported into Kubernetes using the Makefile and cloudtari.yaml file in the scripts/kubernetes directory of the repo. The Makefile also has a lot of other useful commands I needed.

On the main Kubernetes node I installed the nginx web server along with PHP. The index page serves up a choice of ROMs to play. When selected, a launch.php script starts a Python script called start_game.py which instructs Kubernetes to clean up old jobs, find a unique port, and create a unique cloudtari.yaml that is used to start a new job. In order to get the start_game.py script to work from PHP I had to add the webserver to sudoers for that script. There are defintely better ways to take care of that, but for now this works. The host / port which the game is running is passed back to the PHP script which redirects the browser to that server so the user can play the game. When the connection to the webserver is broken Cloudtari exits and the Kubernetes job ends.

Future

The emulator needs a bit of work to be a full blown emulator that can play all Atari 2600 games. It would also be interesting to make it emulate other systems.. maybe Nintendo NES or something. Probably would need to move all the Atari pieces to an Atari2600 class and make objects for other systems that would be emulated.

Copyright 1997-2024 - Michael Kohn