CLASSI~1.DMP
Porting a Pico-8 game to the Atari 2600.

When you're talking Atari 2600, you know there's going to be limitations, that is a given, when you consider the machine has a mere 128 bytes of ram, and most cartridges without bank switching are limited to 2KB ROM space.

So, i obviously decided to try and port my pico-8 game "Marble Rally" to the 2600, because i hate myself or something like that.

First thing was deciding a language, there are basically 3 main options when it comes to modern 2600 development. ASM, the OG, how they did back in day. cc2600, which is a special fork of cc65, which is a c compiler for 6502 and derivatives, the 2600 has a 6507 running at 1.19mhz, and lastly there's batariBasic, which is a dialect of basic designed specifically for the a2600. Since my original Pico-8 game is in Lua, i decided go with basic, one because its been a while since i coded in basic and 2 because i really dont want to deal with 60 year-old asm standards.

The documentation on batariBasic is really good, but the expanded documentation on randomterrain.com is even better. Getting up to speed took no time and in less than 2 hours, i had my little marble rolling on the screen, animated. Then it was time to implement controls.

Controls were about as easy as the sprite itself, you have to consider that there's no "button pressed/released" states, just a boolean "is button down on this frame", so i took the exact same Lua code i had for the Pico-8 and literally just translated it into BASIC, as such:

Lua:

control = function(self)
self.btnnow = btn(4)
if(self.btnprev == false and self.btnnow == true) then
self:buttonpress()
end
self.btnprev = self.btnnow
end,


Basic:

;n = self.btnnow
;p = self.btnpress
;d+1 = self.buttonpress()

if joy0fire then n = 1 else n = 0
if p = 0 && n = 1 then d = d+1
p = n


And that meant i now had my animated marble rolling vertically through the screen and changing directions with the button press.

Now came the biggest challenge, implementing the level map. Pico-8 gives you lots of map-dealing API, including stuff like mset(x,y) to programatically put a tile in the map, having a map that is 8 screens wide pretty much and lots of scrolling / camera properties. So implementing the levels in the Pico-8 were as easy as taking an ASCII level data and parsing it so it correct tile gets a mset according to a loop that goes through the level data with x/y coordinates.

In the 2600 we have, well, nothing. The Atari has a background layer, called the "playfield", which you can kinda scroll in bB, but it just repeats itself in a wrap-around way, you can define the playfield before running any logic and it will persist through the gameplay, its part of the drawscreen sub-kernel.

But we can write directly into the playfield memory, at run time. But it requires specific format, one byte per row per column. And writing all that in real time would bring the framerate way down, and maybe even cause desync, since the TIA (Television Integrated Adapter, aka the 2600 equivalent of a GPU) does everything in terms of scanlines, top to bottom, left to right, you have to "chase the beam" in several cases, and writing 144 bytes 60 times per second (in a NTSC tv) would render the game unplayable. Storing anything in RAM wouldn't be possible, because one, you still need to move from the RAM to the playfield address in the TIA and the system only have 128 bytes of ram, and i need to keep track of things like x/y position, player direction, button status, so, just like in 2026, back in 1977 RAM was a precious resource.

So, first thing first, i learned how to create the playfield. That was easy, bB has a API called playfield. you draw into that and voilá, you have a playfield. This is a bit of high-level trickery considering the 2600 doesn't have any hardware scrolling registers. But bB adds this level of abstraction. Immediately i noticed, the scroll is too fast if i call it 60 times per seconds, so, first lets reduce the frame rate to around 30fps. I create another variable that i use as a timer for that. (and it is important to notice all you get are 25 8-bit int variables, no strings, no floats, so try to keep variable usage as low as possible).
With the speed managed, it comes the part where i really took my time, loading and parsing the data into the playfield. Yes, i can write into playfield memory, but i need to know _what_ to write and _when_ to write. Since each row is made up of 4 bytes and each column is made up of 11 bytes, and that i dont need the top row and neither the bottom row, my columns are 9 bytes each, i decided to take the simplest scrolling approach possible. A rolling scroll, in which each column is loaded into the right most side of the screen and removed once it got past the left most column, almost exactly as most early games does it. So the game "map" is usually what you see on the screen atm + 1 column that will be scrolled into view. This keeps memory usage to a minumum, since i only need to store the next 9 bytes before loading them into the playfield.

But as i was testing it, i realize that i could simple add new columns to the same location of the playfield every full screen scroll, which means i only need to store the right most column of the screen, which is 9 bytes, and iterate through that part of the playfield memory address every 16 columns scrolled by. That also means that i might not need to implement bankswitching, because 2KB of ROM should be enough for a game of similar size as the original PICO-8 version.

I would have to adapt some the levels, but that is ok, in the end of the things, this is more a "port" in the sense it was done in the 80s/90s where devs re-created a game from scratch, sometimes only being able to see the game and not play it. So by doing something similar, i guess i'm doing it the "real way" or some BS like that. Anyway, now i have a scrollable level that i can feed from the data array.

I thought collision detection would be hard, but its solved in bB, with a single line:

if collision(player0, playfield) then goto collisionwall

Yep, you read it right, bB does abstract collision detection in this way. And i can use it for the goal aswell.

Right, the goal, for this i decided to use player1 sprite, because the default 2600 kernel has only 5 sprites. player0 (sprite1) player1 (sprite2), missile0 (sprite3), missile2 (sprite4) and ball(sprite5) can you tell the system was _designed_ to run pong? Because it was and you can tell. So goal is basically player1 (sprite2) stretched vertically and spawned when the scroll counter in the level hits a pre-determined value.
Then another single line collision check and... we're done.

We have a working port of Marble Rally for the Atari 2600. Its not perfect and there are stuff that we can do to make it better. But the MVP is done and if we bolt enough levels we can probably ship it, by this point, but does this sounds right to you? Because it doesn't to me, in fact it doesn't sound like nothing at all... oh right. Sound!

The 2600 has 2 monophonic simple channels. So for this first demo, i did a simple crashing sound when the playfield and player0 collides, nothing major, but this is still a learning project.
You can control the audio channels (0,1) with 3 parameters: Volume, Tone and Frequency. There are presets of Tone and even a "pure" tone.

Now all its left to do, for the demo is add a quick title screen and a death routine.
The title screen seems simple enough. Create a new loop, use the playfield to write something on the screen, do while joystick 0 fire button is not pressed. Rinse and repeat.

For a single level demo, i won't be doing a game over screen, especially since the original Pico-8 version doesn't have one. It will be a small delay/reset when crash and a full reset brings it over the title screen. Simple, yes, but if it works, it works. But honestly, i'm not sure the result is worth the effort, so there's a good chance that Marble Rally 2600 is gonna forever stay a PoC.