In today’s XC-BASIC tutorial, we will add a lot of the essential elements that take our foundations and turn them into a recognisable, playable version of Space Invaders.
- Alien formation and movement.
- Destroying aliens.
- Scores and high scores.
- Destroyable bases.
- Start and game over states.
Along the way we will discuss how retro programming diverges quite a bit from modern coding, especially when it comes to squeezing the game into essentially 1970s hardware!
Play the game so far in your web browser, or edit it yourself in the Retro IDE.
Aliens Assemble!
In part 2, we allowed the player to move around and shoot stuff, but we had nothing to shoot other than junk that appears on the PET startup by default. We need to have some baddies to shoot at.
Space Invaders is quite odd though, especially for the time. Have you ever considered how many bad guys and other game objects are on screen at once?
Even the C64, which has dedicated sprite hardware, was only able to have 8 sprites active on one scan line.
It’s not just about rendering them on screen, either. We need to be able to:
- Move the aliens in formation left, right, and down.
- Detect when an alien has been hit.
- Remove the hit alien.
The obvious Commodore BASIC way to do all of this would be to have the aliens represented by an array or arrays. This would store the X and Y coordinates of each alien, and a state to represent it being active or inactive.
A more modern approach would take that further and consolidate the Alien into a struct or a custom type:
TYPE ALIEN
X AS INT
Y AS INT
STATE AS INT
BULLETX AS INT
BULLETY AS INT
BULLETSTATE AS INT
END TYPE
This should be great as it also encompasses the alien’s ability to shoot back!
But as I eluded to earlier, we need to achieve this at reasonable video game speeds on an 8-bit microprocessor with no additional game graphics hardware. Even using PETSCII characters rather than bitmap graphics, updating the coordinates and then redrawing more than a handful of aliens one by one would really bog the game down.

You can now follow the tutorials and edit the code right in your web browser with the Online Retro IDE
– No downloads, configuration, etc necessary, and it is free!
Manipulating Memory for Movement
Rather than move each alien one by one, then, how about moving them in a group?
Later computers such as the Commodore Amiga, again, had dedicated hardware for moving large chunks of graphical data around called “Blitters” (from “Bit Block Transfer“, apparently). The PET has no such helper silicon, but we can still transfer bytes of memory from one address to another using MemCPY.
In fact, this is how the obstacles in my PETFrog game were moved left and right:

The way it works is you provide the source and destination memory addresses, and how many bytes to write. Depending on whether the addresses overlap or if the source is higher or lower than the destination, you might need to use MEMSHIFT instead:


SRC=SCREENADDRESS+(40*ROW)+ALIEN_LEFT
DST=SCREENADDRESS+(40*ROW)+ALIEN_LEFT-1
MEMCPY SRC,DST,WIDTH
POKE SCREENADDRESS+(40*ROW)+ALIEN_RIGHT,32
This is pretty quick to execute.
Note, however, we need to keep track of
- The left/start column of aliens.
- The right/end column of aliens.
- Width/how many bytes.
And that is for each row of aliens.
We also need to delete the stray alien that was left behind when we shift the formation left or right, and whole rows when we drop the formation down.
There is a companion command to fill memory with a certain value which we can use to erase areas of the screen with spaces:
REM -- clear the screen
MEMSET 1024, 1000, 32
Still, even though I didn’t go that route, it’s a good technique to keep in mind.
What I ended up doing will not be a surprise to anyone who read my article about optimising screen writes. I used the XC-BASIC textat function, which is essentially PRINT but with cursor coordinates.
TEXTAT ALIEN_LEFT-1,ALIEN_TOP,ALIEN_ROW(1)
TEXTAT ALIEN_LEFT-1,ALIEN_TOP+2,ALIEN_ROW(2)
TEXTAT ALIEN_LEFT-1,ALIEN_TOP+4,ALIEN_ROW(3)
TEXTAT ALIEN_LEFT-1,ALIEN_TOP+6,ALIEN_ROW(4)
TEXTAT ALIEN_LEFT-1,ALIEN_TOP+8,ALIEN_ROW(5)
This conveniently replaces the area of the screen we are writing to with exactly what we want to appear all in one operation, though of course, behind the scenes the machine code is still reading and writing individual bytes.
For a little while, I did try to animate the aliens, and the code worked, but I couldn’t find a combination of characters that gave a convincing animation. Seeing as I am using strings for the formations, I simply alternated between strings, updating both when an alien needed to be removed – simple! On the Vic 20 I will try again but instead use custom character definitions so the animation is up to me.
Shooting Aliens
Now we have a challenge. If we are not keeping track of alien’s X/Y coordinates, how do we detect when our bullets hit them and which alien was hit?
Well, we do know if the bullet hit something and where the bullet was when it hit. We just check the screen location and check what we get back:
PEEK(SCREENADDRESS+(40*BY)+BX)
If the bullet is about to share a location with anything other than a blank space, we hit something, and we can figure out what it was.
REM CHECK IF THE BULLET HAS HIT AN ALIEN
IF C=13 OR C=23 THEN CALL ALIEN_KILL()
Once we know we hit an alien, we go ahead and blank the part of the string where the alien sat using a little math. We also place an asterisk on screen as a stand-in for an explosion graphic.
POKE @ALIEN_ROW(((BY-ALIEN_TOP)/2)+1) + BX-ALIEN_LEFT+2, 32
POKE SCREENADDRESS+(40*BY)+BX,42
The way this works is we return the memory address of the string array entry using @
All About the Bases
In the original Space Invaders, the player could hide for protection behind a set of bases. As these bases were hit by aliens or the player, they would crumble and reveal gaps that fire could reach through.
Seeing as my bases are made with full PETSCII blocks, I can’t do the smaller gaps seen in the arcade game without some relatively complex code that I am not sure would even enhance the game. Instead, each character block that is hit immediately vanishes.
The collision detection is the same as hitting an alien or an alien hitting the player, except it is enough to just turn off the bullet or missile. Optionally, we could perhaps make the scoring more granular by also removing points when the base is hit, giving essentially a bonus for completing with bases in tact.
Start and End Screens
If you recall in the previous part, we talked about various sections that appear in any game code. Two big ones after the main game loop are the start and end conditions.
In programming theory, this is called a “State Machine”. As a design pattern, it’s super foundational, and once you are aware of the concept, you see it everywhere.
For our purposes, we need to always be in one of just 3 states:
- Ready to play AKA ‘attract mode’
- Playing (alive)
- Game over (either ‘win’ or ‘lose’)
We don’t need to count “program is running” as a state here, which is maintained on the hardware or operating system level for us.
In a more complex game, there would also be levels or difficulty settings to take into account within the “playing” state.
Our state machine is simple once we know the few states that we need to care about:
- Show the main screen
- Initialise/reset variables to defaults
- Loop while not
GAME_OVER - Show end screen:
- If
LIVES > 0show win screen (unless the player quits) - Otherwise, show the losing screen
- If
As all of this is wrapped in an infinite loop, we simply create subroutines for the welcome screen and end screen, and they naturally execute before and after the core game loop.
For now, they simply display text, we will finesse them once the main heavy lifting is done.
What Next?
In the next part, we will turn this from a playable demo into something that feels much more like a real Space Invaders game.
We’ll add alien missiles, so the enemies can fire back, handle collisions with the player and the bases, add a simple player explosion effect, and make the alien formation shrink as rows and edge columns are cleared, allowing the formation to drop when they collectively hit the edges of the play field.
At this stage, we also must start separating movement and firing into timed systems, which gives us much better control over speeds and difficulty levels.




XC-BASIC Shoot ’em Up: Programming the PET Part 2