Lader (Atari 8 Bit, C64, ZX Spectrum) by Marco's Retrobits
A downloadable game
# Lader
## BASIC 10 Liner platformer for 8 bit computers
by **Marco Varesio** ( Marco's Retrobits (https://retrobits.itch.io/))
Download/play online (https://retrobits.itch.io/lader)
YouTube channel (https://www.youtube.com/@marcosretrobits)
English language blog (https://retrobits.altervista.org/)
Italian language blog (https://somebitsofme.altervista.org)
**Lader (https://retrobits.itch.io/lader)** (in Piedmontese language, *làder* means thief, robber) is a simplified version of the classic CP/M game called Ladder (https://en.wikipedia.org/wiki/Ladder_(video_game)). You are a treasure hunter who sneaked in an old ruined and abandoned mansion, in search of money and glory. In order to find the treasure, you'll have to walk on the platforms and climb the ladders in each room. But beware, the house is haunted by ghosts, whose touch is lethal!
Lader has been coded in **ugBASIC** (https://ugbasic.iwashere.eu/) for the 14th Edition (2025) of the **BASIC 10 Liner Contest** (https://www.homeputerium.de/), EXTREM-256 category and is currently available for the following 8 bit home computers: **Sinclair ZX Spectrum**, **Commodore 64** and **Atari**. In the ZX Spectrum version, there are at most 2 ghosts simultaneously on screen, while in the Commodore 64 and Atari versions there might be up to 4 ghosts.
## Instructions,
The character controlled by the player is represented by a "p" when facing right, "q" when facing left. The aim of the game is to reach the treasure, represented by the character "$". To do this, the player must move on the platforms ("="), go up/down the ladders ("H") and climb over the walls ("|"), using the following keys:
* E: Up
* D: Down
* O: Left
* P: Right
* SPACE: Jump
Your task is made difficult by ghosts, represented by means of the character "o". Ghosts appear where the characters "V" are displayed and disappear near the asterisks "*". Contact with ghosts is lethal and will result in the loss of a life.
The current level number is displayed at the top left corner of the screen, while remaining lives counter is shown at the top right corner. When there are no more lives left, the game will start again from the 1st level.
Due to the contest constraints on the program length, the game features only 3 unique level screens.
## Program listing
Below is the full source code of the program, completed with comments explaining how it works in detail:
```basic
OPTION DEFAULT TYPE SIGNED BYTE : ' Set the default type of variables to signed byte
' Levels data
' For each level, data is encoded in the following way:
' The first 2 values contain the coordinates of the player start position
' -1 indicates that the following pairs of values contain the coordinates of the enemies start positions "V"
' -2 indicates that the following pair of values contain the coordinates of the treasure "$"
' -3 indicates that the following pairs of values contain the coordinates of the enemies stop positions "*"
' -4 indicates that the following pairs of values contain the coordinates of the walls "|"
' -5 indicates that the following triplets of values contain the coordinates and length of the platforms "="
' -6 indicates that the following triplets of values contain the coordinates and height of the ladders "H"
' -8 marks the end of level data
' -9 marks the end of all levels data
0 DATA _
1,22,-1,10,12,22,12,-2,31,22,-3,0,22,-5,0,23,32,-8, _
5,22,-1,14,0,-2,24,0,-3,0,22,31,22,-5,3,3,25,0,8,21,23,8,9,3,13,10,16,13,10,0,18,8,10,18,9,21,18,8,0,23,32,-6,5,3,6,5,12,6,11,7,3,11,13,1,24,1,2,24,7,6,24,17,6,-8, _
3,22,-1,5,6,26,6,-2,30,4,-3,9,22,20,22,22,22,-4,4,6,9,17,9,18,12,10,16,21,16,22,27,6,-5,0,7,10,11,7,10,22,7,10,0,11,9,10,11,3,15,11,6,22,11,10,0,15,8,11,15,9,22,15,10,0,19,7,9,19,11,22,19,10,0,23,32,-6,1,6,5,1,14,5,30,5,2,30,10,5,30,18,5,-8,_
-9
' Some useful character codes
cH=ASC("H")
cBl=ASC(" ")
cDl=ASC("$")
cAs=ASC("*")
cV=ASC("V")
cq=ASC("q")
cp=ASC("p")
cPi=ASC("|")
BORDER GREEN: PAPER BLACK: PEN GREEN: CLS : ' Good old green phosphor look and feel
' Screen initialization. The game can be compiled for virtually any platform supported by ugBASIC
' with at least 24 lines and 32 columns in text mode.
' On platforms with a greater screen resolution, the console command is used for centering
' the output on the screen.
IF SCREEN COLUMNS > 32 THEN:CONSOLE (SCREEN COLUMNS-32)/2, 0, (SCREEN COLUMNS-32)+32, 24:ENDIF
DIM t AS WORD : ' Used for timing
DIM d(2) : ' Player start position coordinates
DIM e(2,2) : ' "Enemies" start positions coordinates
DIM o(5,4) : ' Animated game objects: player (at index 0) and enemies (at indexes 1,2,3,4)
: ' The 2nd dimension contains: x and y coordinates, horizontal speed h, status s
: ' Horizontal speed can be: +1 (moving right), 0 (not moving horizontally), -1 (moving left).
' Status can be: 0:run/climb/still; 1:fall; 2:jump; 3:idle (only enemies can be idle)
n=4 : ' Max number of enemies simultaneously on screen
PROCEDURE z : ' This procedure limits the enemies number to 2.
SHARED n : ' Since I noticed that on Z80 platforms (MSX,
n=2 : ' ZX Spectrum) the game slows down with n>2, the
END PROC : ' procedure is invoked only for those platforms
z[] ON CPUZ80 : ' (ON CPUZ80)
DIM m(24,32) : ' Current level map (array of 32x24 character codes)
w=1 : ' Highest level reached (hi score)
DO : ' External endless loop. As soon as a game ends, a new one is immediately started
l=5 : ' Initialize lives left
k=0 : ' Initialize current level number
f=0 : ' Used for reading platform length and ladder height data
RESTORE 0 : ' Start reading level data from the beginning (1st level)
DO : ' Levels loop. Executed until the player has no more lives left
CLS
' ----------------------
' Load and display level
' ----------------------
INC k
' Clear current level map by initializing all items to blank " "
FOR x=0 TO 31
FOR y=0 TO 22
m(y,x)=cBl
NEXT
NEXT
a=0 : ' Which kind of level data is being read (ladders, walls, platforms...)
i=0 : ' Iterator used in for loops
ec=0 : ' Enemies count
DO : ' Loop through the g() array, decode level data and disply level on screen
: ' Decoded level data is used for initializing variables and filling current level map m() accordingly
READ x:IF x=-9 THEN:RESTORE 0:READ x:ENDIF : ' Read the next value from levels data and store it into x. If the end
: ' of levels data list is reached, start again from the beginning
IF x>=0 THEN : ' A value > 0 is a column number, so the next value is a line number
READ y:LOCATE x,y
IF a=0 THEN : ' Reading the player start position
d(0)=x:d(1)=y:c=cp : ' Store the player start position coordinates in d(0), d(1)
ELSE IF a=-1 THEN : ' Reading the enemies start positions
e(ec,0)=x:e(ec,1)=y:INC ec : ' Store the enemies start positions in the e() array
c=cV:m(y,x)=c
ELSE IF a=-2 THEN : ' Reading the treasure position
c=cDl:m(y,x)=c
ELSE IF a=-3 THEN : ' Reading the enemies stop positions
c=cAs:m(y,x)=c
ELSE IF a=-4 THEN : ' Reading the walls positions
c=cPi:m(y,x)=c
ELSE IF a=-5 THEN : ' Reading the platforms positions
READ f:FOR i=x TO x+f-1:m(y,i)=ASC("="):PRINT"=";:NEXT
ELSE IF a=-6 THEN : ' Reading the ladders positions
READ f:c=cH:FOR i=y TO y+f-1:m(i,x)=c:LOCATE x,i:PRINT"H";:NEXT
ENDIF
IF a>-5 THEN:PRINT CHR$(c);:ENDIF
ELSE
a=x
IF (x=-8)THEN:EXIT:ENDIF : ' -8 is the end of level data; exit loop
ENDIF
LOOP
a=0 : ' Game status: 0:running; 1:level completed; 2:life lost
' ----------
' Play level
' ----------
DO
' Clear positions initially occupied by player or enemies
' and initialize player and enemies statuses
FOR i=0 TO n
LOCATE o(i,0),o(i,1): PRINT CHR$(m(o(i,1),o(i,0)));
IF i=0 THEN : ' Player
o(0,0)=d(0) : ' Set player initial x position
o(0,1)=d(1) : ' Set player initial y position
o(0,2)=1 : ' Set player horizontal speed (1: moving right)
o(0,3)=0 : ' Set player status to 0 (run/climb/still)
ELSE : ' Enemy
o(i,3)=3 : ' Set enemy status to 3 (idle)
ENDIF
NEXT
' Print lives left count in the top right corner
LOCATE 31,0:PRINT l;
' Print level number in the top left corner
LOCATE 0,0:PRINT k;
j=0 : ' Jump timer. Used to synchronize player jumps
oc=0 : ' Enemies spawn timer. Used for spawning enemies
DO : ' Game loop
t=TIMER : ' Timer. Used to control game speed
FOR i=0 TO n : ' Iterate over game objects
x=o(i,0) : ' Read object x position
y=o(i,1) : ' Read object y position
h=o(i,2) : ' Read horizontal speed
s=o(i,3) : ' Read status
v=0 : ' Vertical speed (1: moving down; 0: not moving vertically; -1: moving up)
IF s=3 THEN : ' If the enemy is idle and the spawn timeout oc is 0, spawn enemy at a random start position in e()
IF oc=0 THEN:INC oc:r=0:IF ec>1 THEN:r=RND(2):ENDIF:o(i,0)=e(r,0):o(i,1)=e(r,1):o(i,2)=((i AND 1) = 1)-((i AND 1) = 0):o(i,3)=1:ENDIF
ELSE
IF i<>0 THEN : ' Check if current object is an enemy and if it is hitting the player.
IF x=o(0,0) AND y=o(0,1) THEN:LOCATE x,y:PRINT "x";:a=2:EXIT 2:ENDIF : ' If so, set game status to 2 (life lost) and exit loop
ENDIF
IF s<2 THEN
' Not jumping
c=m(y+1,x) : ' Check the position below the object
IF c=cBl THEN : ' The position below the object is empty (blank space)...
s=1:v=1 : ' ...so the object is falling down
ELSE : ' The position below the object is not empty
IF i<>0 THEN : ' Current object is an enemy
IF c=cH THEN : ' The character below the enemy is a ladder
IF s=0 THEN : ' The enemy is passing over a ladder...
IF RND(2)=0 THEN:s=1:v=1:ENDIF : ' ...randomly choose whether the enemy will go down the ladder
ELSE : ' The enemy is already on a ladder....
s=1:v=1 : ' ...keep descending the ladder
ENDIF
ELSE : ' The position below the enemy is neither empty nor a ladder
IF s=1 OR v=1 THEN : ' If the enemy was falling or descending a ladder...
r=RND(2):h=(r=0)-(r=1):v=0:s=0 : ' stop descending and choose a random horizontal direction
ENDIF
ENDIF
ENDIF
IF i=0 THEN : ' Current object is the player
s=0 : ' Move and update status according to the key pressed
IF KEY STATE(KEY SPACE) AND s=0 AND j=0 THEN:j=-1:s=2:ENDIF : ' SPACE: jump
IF KEY STATE(KEY E) THEN:IF m(y,x)=cH THEN:h=0:v=-1:ENDIF:ENDIF : ' e: if on a ladder, climb it
IF KEY STATE(KEY D) THEN:IF c=cH THEN:h=0:v=1:ENDIF:ENDIF : ' d: if on a ladder, descend it
IF KEY STATE(KEY P) THEN:h=1:ENDIF : ' p: go right
IF KEY STATE(KEY O) THEN:h=-1:ENDIF : ' o: go left
ENDIF
ENDIF
ELSE
' Jumping (only the player can jump)
IF j<0 THEN
DEC j:IF j>-4 THEN:DEC v:ENDIF:IF j=-5 THEN:j=0:s=1:ENDIF : ' Update status and vertical speed based on the jump timer
ENDIF
ENDIF
IF s<>1 AND v<>1 THEN x=x+h : ' Update horizontal coordinate x : 'x=x-(h*(s<>1)):'bug?
IF x=32 OR x=-1 OR m(y,x)=cPi THEN:h=-h:x=x+h:ENDIF : ' If reaching the screen left or right border, go back
y=MAX(0,y+v) : ' Do not go beyond the top of the screen
IF i=0 THEN : ' Current object is the player...
p=CHR$(cq+(h=1)) : ' ...display it using 'p' or 'q' depending on his horizontal direction
ELSE : ' Current object is an enemy...
p="o" : ' ...display it using the 'o' character
ENDIF
LOCATE o(i,0),o(i,1): PRINT CHR$(m(o(i,1),o(i,0))); : ' "Delete" the object from its old position on the screen
LOCATE x,y: PRINT p; : ' Print the object to its new position
c=m(y,x) : ' Check character at current object position in the level map for collision detection
IF i=0 THEN : ' Player
IF c=cDl THEN:a=1:EXIT 2:ENDIF : ' Player reached the treasure. Set game status to 1 (level completed) and exit loop
ELSE : ' Enemy
IF c=cAs THEN:s=3:LOCATE x,y:PRINT "*";:ENDIF : ' Enemy reached a stop position. Become idle
IF x=o(0,0) AND y=o(0,1) THEN : ' Enemy hits the player...
LOCATE x,y:PRINT "x";:a=2:EXIT 2 : ' ...so, set game status to 2 (life lost) and exit loop
ENDIF
ENDIF
' Update game object properties
o(i,0)=x
o(i,1)=y
o(i,2)=h
o(i,3)=s
ENDIF
NEXT
INC oc:IF oc>=20 THEN oc=0 : ' Reset the enemy spawn timeout
WHILE ABS(TIMER-t)<4:WEND : ' Wait for (at least) 4 frames
LOOP : 'Game loop end
WAIT 1000 MS
' Check game status
IF a=1 THEN:EXIT:ENDIF : ' Level completed. Exit loop so that next level is loaded
IF a=2 THEN:DEC l : ' Life lost
IF l=-1 THEN : ' No more lives...
LOCATE 0,0:PRINT "GAME OVER" : ' Game is over
IF k>w THEN:PRINT "LEVEL=";k;". CONGRATS!":w=k:ENDIF : ' Check if new hi score
WAIT 1500 MS:EXIT 2 : ' Exit and start again from 1st level
ENDIF
ENDIF
LOOP
LOOP
LOOP
```
The source code has been stripped of comments and spaces and compressed using the language abbreviations. The resulting program is made up of 10 lines, each with a maximum length of less than 256 characters; therefore, it is eligible for the EXTREM-256 category:
```basic
0 OpDftTySgndBy:Da1,22,-1,10,12,22,12,-2,31,22,-3,0,22,-5,0,23,32,-8,5,22,-1,14,0,-2,24,0,-3,0,22,31,22,-5,3,3,25,0,8,21,23,8,9,3,13,10,16,13,10,0,18,8,10,18,9,21,18,8,0,23,32,-6,5,3,6,5,12,6,11,7,3,11,13,1,24,1,2,24,7,6,24,17,6,-8,3,22,-1,5,6,26,6,-2,30
1 Da4,-3,9,22,20,22,22,22,-4,4,6,9,17,9,18,12,10,16,21,16,22,27,6,-5,0,7,10,11,7,10,22,7,10,0,11,9,10,11,3,15,11,6,22,11,10,0,15,8,11,15,9,22,15,10,0,19,7,9,19,11,22,19,10,0,23,32,-6,1,6,5,1,14,5,30,5,2,30,10,5,30,18,5,-8,-9:cH=Ax("H"):cBl=Ax(" ")
2 cDl=Ax("$"):cAs=Ax("*"):cV=Ax("V"):cq=Ax("q"):cp=Ax("p"):cPi=Ax("|"):BoGre:PaBl:PnGre:Cl:IfScCms>32Th:Cns(ScCms-32)/2,0,(ScCms-32)+32,24:Ei:Dit AsWo:Did(2):Die(2,2):Dio(5,4):n=4:PROCEDURE z:Srn:n=2:EePrb:z[]ON CPUZ80:Dim(24,32):w=1:Do:l=5:k=0:f=0:Rer0
3 Do:Cl:INCk:Fox=0To31:Foy=0To22:m(y,x)=cBl:Nx:Nx:a=0:i=0:ec=0:Do:R#x:Ifx=-9Th:Rer0:R#x:Ei:Ifx>=0Th:R# y:Lcx,y:Ifa=0Th:d(0)=x:d(1)=y:c=cp:ElIfa=-1Th:e(ec,0)=x:e(ec,1)=y:INCec:c=cV:m(y,x)=c:ElIfa=-2Th:c=cDl:m(y,x)=c:ElIfa=-3Th:c=cAs:m(y,x)=c:ElIfa=-4Th
4 c=cPi:m(y,x)=c:ElIfa=-5Th:R#f:Foi=x Tox+f-1:m(y,i)=Ax("="):?"=";:Nx:ElIfa=-6Th:R#f:c=cH:Foi=y Toy+f-1:m(i,x)=c:Lcx,i:?"H";:Nx:Ei:Ifa>-5Th:?Ch(c);:Ei:El:a=x:If(x=-8)Th:Ex:Ei:Ei:Lp:a=0:Do:Foi=0Ton:Lco(i,0),o(i,1):?Ch(m(o(i,1),o(i,0)));:Ifi=0Th:o(0,0)=d(0)
5 o(0,1)=d(1):o(0,2)=1:o(0,3)=0:El:o(i,3)=3:Ei:Nx:Lc31,0:?l;:Lc0,0:?k;:j=0:oc=0:Do:t=Tmr:Foi=0Ton:x=o(i,0):y=o(i,1):h=o(i,2):s=o(i,3):v=0:Ifs=3Th:Ifoc=0Th:INCoc:r=0:Ifec>1Th:r=Rr(2):Ei:o(i,0)=e(r,0):o(i,1)=e(r,1):o(i,2)=((i An1)=1)-((i An1)=0):o(i,3)=1:Ei
6 El:Ifi<>0Th:Ifx=o(0,0)Any=o(0,1)Th:Lcx,y:?"x";:a=2:Ex2:Ei:Ei:Ifs<2Th:c=m(y+1,x):Ifc=cBl Th:s=1:v=1:El:Ifi<>0Th:Ifc=cH Th:Ifs=0Th:IfRr(2)=0Th:s=1:v=1:Ei:El:s=1:v=1:Ei:El:Ifs=1ORv=1Th:r=Rr(2):h=(r=0)-(r=1):v=0:s=0:Ei:Ei:Ei:Ifi=0Th:s=0
7 IfKyStt(KySp)Ans=0Anj=0Th:j=-1:s=2:Ei:IfKyStt(KyE)Th:Ifm(y,x)=cH Th:h=0:v=-1:Ei:Ei:IfKyStt(KyD)Th:Ifc=cH Th:h=0:v=1:Ei:Ei:IfKyStt(KyP)Th:h=1:Ei:IfKyStt(KyO)Th:h=-1:Ei:Ei:Ei:El:Ifj<0Th:Dcj:Ifj>-4Th:Dcv:Ei:Ifj=-5Th:j=0:s=1:Ei:Ei:Ei:Ifs<>1Anv<>1Thx=x+h
8 Ifx=32ORx=-1ORm(y,x)=cPi Th:h=-h:x=x+h:Ei:y=Mx(0,y+v):Ifi=0Th:p=Ch(cq+(h=1)):El:p="o":Ei:Lco(i,0),o(i,1):?Ch(m(o(i,1),o(i,0)));:Lcx,y:?p;:c=m(y,x):Ifi=0Th:Ifc=cDl Th:a=1:Ex2:Ei:El:Ifc=cAs Th:s=3:Lcx,y:?"*";:Ei:Ifx=o(0,0)Any=o(0,1)Th:Lcx,y:?"x";:a=2:Ex2
9 Ei:Ei:o(i,0)=x:o(i,1)=y:o(i,2)=h:o(i,3)=s:Ei:Nx:INCoc:Ifoc>=20Thoc=0:WhAb(Tmr-t)<4:We:Lp:Wt1000MS:Ifa=1Th:Ex:Ei:Ifa=2Th:Dcl:Ifl=-1Th:Lc0,0:?"GAME OVER":Ifk>w Th:?"LEVEL=";k;". CONGRATS!":w=k:Ei:Wt1500MS:Ex2:Ei:Ei:Lp:Lp:Lp:'Lader - Marco's Retrobits 2025
```

## Emulators instructions
Lader is available for several 8 bit computers, in emulators-friendly file formats:
* Commodore 64: **lader10l.c64.prg**
* Atari 400/800: **lader10l.atari.xex**
* ZX Spectrum: **lader10l.zx.tap**
Please follow the platform specific instructions in the sections below for details on how to load the desired version of Lader in the most popular emulators.
Once the program is loaded, the ugBASIC runtime will prompt you for a command:
```
ugBASIC Runtime version 1.XY.Z
READY
```
To start the game, type <kbd>run</kbd> and tap <kbd>Enter</kbd>.
To view the listing, type <kbd>list</kbd> and tap <kbd>Enter</kbd>.
### Platform specific instructions
#### Commodore 64 (VICE emulator)
* Start the VICE C64 executable (e.g. x64sc.exe)
* Either:
* Open the lader10l.c64.prg file by choosing "File" -> "Smart attach...",
* When the "READY" prompt is shown, type <kbd>run</kbd> followed by <kbd>Enter</kbd>.
* Or:
* Drag the lader10l.c64.prg file and drop it into the VICE C64 window.
This will automatically start the ugBASIC runtime.
#### Atari (Altirra emulator)
* Start the Altirra executable (e.g. Altirra64.exe)
* Either:
* Open the lader10l.atari.xex file by choosing "File" -> "Boot Image...";
* Or:
* Drag the lader10l.atari.xex file and drop it into the Altirra window.
#### Sinclair ZX Spectrum (FUSE - the Free Unix Spectrum Emulator)
* Start the Fuse executable (e.g. fuse.exe)
* Select the "Spectrum 48K" model in "Machine" -> "Select..."
* Make sure that automatic loading of tape image files is enabled, by checking the corresponding options in
"Options" -> "Media..."
* Either:
* Open the lader10l.zx.tap file by choosing "File" -> "Open...";
* Or:
* Drag the lader10l.zx.tap file and drop it into the Fuse window.
If your emulator of choice does not support automatic tape loading, after mounting the tape image you
must manually issue the tape loading command, by pressing the J key, followed by CTRL + P twice (the SYMBOL SHIFT ZX Spectrum key is usually mapped to CTRL) and then by ENTER.
Updated | 20 hours ago |
Published | 1 day ago |
Status | Released |
Author | BASIC 10Liner |
Genre | Action |
Tags | 10iner, 8-Bit, atari, basic, Commodore 64, ZX Spectrum |
Comments
Log in with itch.io to leave a comment.
very good
thank you!!!