My first ZX81 arrived under the Christmas tree in 1982.
It came with a selection of games, including my beloved 3D Monster Maze (mine was the New Generation Software version, which means it must have been 1982 rather than 1981).
Another tape it came with and that I played a lot was Psion's "Fantasy Games", republished by Sinclair Research.
This included two games, one on each side. "Perilous Swamp" and "Savage Island".
I am pretty sure on my copy Savage Island on side B never loaded, I don't remember playing it, and it doesn't load now, so it was probably like that from day one.
But that wasn't important, Perilous Swamp did load, and I really liked it.
It is a very simple idea. Rescue the Princess and escape.
You move around a 9x9 map, one square at a time, in any direction, including diagonals.
Every time you move, you meet a monster, and you have a choice to fight, bribe or run away.
Sometimes you are lucky.
Sometimes you are not.
There are various other things that can happen, but I don't want to spoil any of those.
It is a fairly basic game, in fact it is a BASIC game.
When you exit, you are returned to a prompt, and I can't help but press K to get a listing and see what is going on.
Sometimes you just get a lot of machine code and there is nothing you can do with it, e.g. in 3D Monster Maze (although look further and that is partly BASIC - the time consuming maze generation bit is written in BASIC for some inexplicable reason).
In this case, you can see Perilous Swamp is an entirely BASIC program.
(notice the gap at the start between 30 and 100. I wonder if there were originally some developer credits as part of the title screen?)
I remember back in 1983 or 1984 editing this program on my ZX81 to be a "Five Doctors" version. I had the swamp being the Death Zone, and the various monsters being Doctor Who monsters. I wonder if one day I will find the tape I saved that on?
It seems appropriate that I am posting this on 23rd November, the 62nd anniversary of the first episode, and (almost) the 42nd anniversary of that 20th anniversary Special from 1983. I will probably watch that later today, by far the episode I have watched most, probably the episode of anything that I have watched most.
That raises an interesting question.
Can I convert that to another computers version of BASIC?
I wonder if I could make a VIC20 version?
It has a lot going for it.
- I had this on tape in the 1980s for my ZX81
- It was one of the first games I got with the ZX81
- It was therefore one of the first games I ever played
- It's a great game
- I still like to play it
- I can actually complete it
- It is in BASIC, so should be reasonably easy to convert
I very briefly considered whether it would be more efficient to re-write it in C, but honestly, I think the BASIC conversion will be easier and the program will probably be more than fast enough (and C sucks at string handling, specially when dealing with the finer points of string handling in CC65)
BASIC it is then.
The first challenge was getting hold of the listing.
There is a listing on this site:
I couldn't work out how to extract that, it appears to be a script generated picture of a listing, rather than a text file.
Instead, I found a plugin for Visual Studio Code which converts between .p files and .bas files (in either direction).
That game me a text file to start with, great.
Looking through it, it is mostly pretty standard BASIC. I spotted a few things I would need to address, but a lot of it would require minimal changes.
Anyone who has seen the Usborne serial of computer games books knows the procedure here.
In those books, the listing were printed for the ZX81 (later "a generic version of BASIC")
The symbols at the side indicated changes require for certain systems.
Here you can see the ▲ indicating VIC changes are mostly
CLS 🡆 PRINT CHR$(147)
and
RND 🡆 RND(1)
There are also some things like the LET statements can be omitted.
LET A=42 🡆 A=42
One thing to be aware of is the line length in Commodore BASIC is limited to 80 characters, and so several of the longer lines of text will need to be truncated.
In most cases that is fine, I am going to have to split the lines of text up anyway, but I hit a snag with lines like 135.
135 LET V$="10 10 SILVER SPOONS30 A JEWELLED SWORD50 A JAR OF RUBIES 200A TREASURE CHEST50 50 SILVER PIECES100100 GOLD PIECES 75 A BOX OF JEWELS 001A FAIR PRINCESS "
This defines a string containing a list of valuable items, 3 characters for a value, and 16 for a description.
"200" + "A TREASURE CHEST"
I suppose I could have gotten away with that by building up the string over multiple shorter lines
135 V$="10 10 SILVER SPOONS30 A JEWELLED SWORD50 A JAR OF RUBIES " 136 V$=V$ + "200A TREASURE CHEST50 50 SILVER PIECES100100 GOLD PIECES " 137 V$=V$ + "75 A BOX OF JEWELS 001A FAIR PRINCESS "
I decided to see how it is used, and it is only referenced in lines 1140 and 1150
1140 PRINT "IS GUARDING ";V$(I*19-15 TO I*19) 1150 LET P=VAL V$(I*19-18 TO I*19-16)
This is using the ZX81 BASIC string manipulation to split up the strings into the description and the value.
I could have replaced those with Commodore BASIC MID$ commands, but maybe there is a better option?
!- v,v$ data 20000 data 10, "10 silver spoons" 20010 data 30, "a jewelled sword" 20020 data 50, "a jar of rubies" 20030 data 200,"a treasure chest" 20040 data 50, "50 silver pieces" 20050 data 100,"100 gold pieces" 20060 data 75, "a box of jewels" 20070 data 1, "a fair princess"
Here I have reformatted that into a series of DATA statements at the end of the program.
I then need to change V$ into a V$ array and add a V array for the values. Commodore BASIC arrays are actually 0-n size, so I could have used 0-7 but I frivolously wasted the 0th element to save having to subtract 1 on every access.
134 dim v(8) : dim v$(8) 135 for n = 1 to 8 : read v(n), v$(n) : next n
I can then simplify the access to
1140 print "is guarding "; v$(i) 1150 p=v(i)
I also need to add a RESTORE command at the start of that so that if the player restarts the game, it re-reads the DATA statements from the start.
I tested that by printing them out.
Oh, I should point out at this stage that I had lowercased the entire BASIC program. Lowercase characters in the .bas files appear as uppercase in the default font. Yeah, it's a but annoying, but lets keep things easy.
There were also a few other string arrays for the monster names and adjectives etc.
I initially added the title screen, instructions and the generation and drawing of the map.
Well, that's a good start, but lets fix the characters.
The only change I made was using ♥ for the princess, rather than *.
I did think about swapping the swamp and edge characters, because it would seem to make more sense to have a solid border and the 50% grey representing the swampy bits, but I wanted to keep it mostly original, even down to the odd spacing of the X= YOU and the position of the N on the compass.
(that is the same on Savage Island, so I presume it is intentional.)
OK, time to add the rest of the code.
At this stage I was reasonably confident, so I just cut and pasted the whole thing.
I did a search and replace to remove the LET statements and change the SCROLLs to REM SCROLL for the moment.
The ZX81 code is peppered with SCROLL statements.
These are not required on other machines, they scroll automatically when you get to the bottom of the page.
However, in many cases those ended up being replaced with PRINT statements to leave the one line gap that the scrolling would have done.
I went through converting the RND
820 LET I=INT (RND*12)
to RND(1)
820 i=int (rnd(1)*12)
I was deliberately trying not to look at the logic, I don't want to spoil the game by learning how it weights its choices.
There were a few other changes related to the strings, X$(1) is used to get the first character of X$, after reading the keyboard
1355 INPUT X$ 1420 IF X$(1)="N" THEN LET Y=Y-1 1430 IF X$(1)="S" THEN LET Y=Y+1
It's quite cleverly done, it is checking the first character, so if you type N or NE or NW, it will move north.
They then check the last character of X$.
1400 IF X$(LEN (X$))="W" THEN LET X=X-1 1410 IF X$(LEN (X$))="E" THEN LET X=X+1
So if you type W or SW or NW, then it will also move west. Quite a neat way of allowing diagonal movement.
String access doesn't work like that in Commodore BASIC, so I generated X1$ and X2$ from calls to LEFT$ and RIGHT$
1370 x1$ = left$(x$,1) : x2$ = right$(x$,1) 1400 if x2$="w" then x=x-1 1410 if x2$="e" then x=x+1 1420 if x1$="n" then y=y-1 1430 if x1$="s" then y=y+1
Back at the start, there were two PAUSE commands. These are unusual to ZX81 BASIC (I don't think I have seen that in any other version of BASIC).
30 PAUSE 200 100 CLS
They wait for you to press a key, but you supply a timeout, so if you haven't pressed a key in 200 frames (4 seconds on the PAL UK 50Hz machine), it will continue to clearing the screen anyway.
80 p=200
90 gosub 10000
!- instructions
100 print "{clear}"
I wanted to keep that functionality, so wrote something similar as a subroutine at the end of the program. Set P to the frame count, then GOSUB 10000.
!- pause routine 10000 t0 = ti 10010 if ti-t0 > p then return 10020 get k$ 10030 if k$<>"" then return 10040 goto 10010
I will renumber things later, for now I was trying to keep to the same line numbers and add things like pause and the data statements well past the end.
I am also writing this in CBM PRG Studio, so I can add the !- style comments to the BAS file and they will be ignored when generating the PRG files.
I have split things up where I can, when there is an unconditional jump (a GOTO), I call that the end of the function, add a space and a comment, once I work out what the next bit is about
There were a few occasions where the same thing was tested twice.
560 IF K>C THEN PRINT "BUT YOU ONLY HAVE ";C;" POINTS" 580 IF K>C THEN GOTO 500
Commodore BASIC has compound statements, so I could change that to a single line (line length premitting)
560 if k>c then print "but you only have";c;"points" : goto 500
I also removed the spaces either side of printing the point score as there is space padding either side of numbers in Commodore BASIC.
There were various text changes to fit the 22 character screen size on the VIC20, from the 32 character width on the ZX81.
I tried to avoid breaking words across the end of line, so things got a bit more spaced out.e.g.
Things were pretty much there. I tweaked lots of line spacing, and I think they all line up now.
The final code is about 8.5K, so fits into a VIC20 with an 8K expansion (i.e. 11K available)
I have given myself a credit below the PSION one.
I left in the "A New Adventure Game" bit. It wasn't even "New" in 1982 when Sinclair Research repackaged it, it was originally from 1981.
I made a slight change to the instructions, I initially print those up.
Whilst the user is reading that, I setup all the variables, read the DATA statements etc.
That only take a couple of seconds. Then I display the "PRESS ANY KEY" message.
Note, on the ZX81 version, when it says "ANY" key, what it actually means is almost "ANY" key. Fair enough that SHIFT won't register, but if you press SPACE, that works like BREAK and stops the program.
So if you press (or keep pressing) SPACE on this screen, you have to keep restarting the game, possibly several time, until you remember to not press BREAK.
Ask me how I know.....
The ZX81 version goes into "FAST" mode when setting up the variables, and again when drawing the map. I have just removed those lines, since the code runs faster in Commodore BASIC than it does in ZX81 BASIC in fast mode.
Possibly even too fast. The ZX81 has a sort of nice "teleprinter" type effect where each character appears one at a time.
This is because it is using SCROLL, and that converts the screen to a partial display file (which you can see here on a Minstrel 3 with the grey scale jumper set).
Every character added causes it to reset the end of the display file. Not very efficient, but actually not a bad effect for this sort of application.
(although side note, try to exit the game on a ZX81 with a screen full of text, then watch how it clears the screen from the top down, shuffling each line around as it goes, taking about 10 seconds)
That was it done. I have tested it through many times, and asked a couple of friends to give it a go (one a seasoned Swamp player, one hadn't seen it before), and so far I haven't spotted any issues.
I renumbered the code to keep things tidy (it ended up with 447 lines 10 - 4470 if you are interested. I can tell you are), and have uploaded it and a built version to my github.
But wait, I hear you say. Commodore BASIC runs on more machines than the VIC20.
Indeed it does.
Presenting the PET version.
Here I had to make various changes to the line lengths again, as the PET has a luxurious 40 character display, so many of the 22 character lines could be glued together.
It works quite nicely, you can't beat a bit of greenscreen.
This is certainly going into my "games to test on a PET" folder.
But wait, you cry again. What about the C64?
Well, this is a BASIC program, and it is portable between machines. The C64 is also 40 characters, so I can load the PET version on a C64, as long as you load it in the correct way
If you use
LOAD "SWAMPPET",8,1
That will load it to the address in the file ($0401) which is not suitable for the C64 and would overwrite the screen.
The correct way is
LOAD "SWAMPPET",8
That will load the program at the default address for the C64 ($0801).
That will then run fine with no changes at all to the BASIC program. Under the hood, the BASIC program has been relinked from the PET load address of $0401 to the C64's $0801.
All I have done to generate a C64 version is edit that title page and save it.
This is just to show how portable BASIC can be. I have never been a fan of the C64 default colours, those at least would need to be changed, and just like the VIC, you could go all out and use custom graphics and colours if you wanted. I know I could change it, but I wanted to keep the code simple, so no place for POKEs to set the colours here.
Other than the C64 on the title page and the load address, the program is identical to the PET version.
Now that it has been saved from a C64, the LOAD address in the file is correct, so you can LOAD it with ,8 or ,8,1.
But wait.....
Yes, OK, I'll do a TED version as well.
The 40 column code is just over 8K, so will fit on the Commodore 16 and the plus/4.
I prefer these colours, the same as the ZX81, but with the colourful border.
One change was required here, the character code for the edge character was different on the TED machines, so I had to change that. As before, the load address was changed under the hood to the TED's $1001, but again no changes to the vast majority of the code.
All of those versions are on the github if you want to try them out.
You can also play the original ZX81 version online here:
I suppose if you wanted to, you could convert that for other BASIC machines, it's pretty generic Microsoft BASIC based, so should port anywhere else reasonably well. I have done a version for MS BASIC on the RC2014, just need to set one up to test that before uploading it.
And no, I am not writing a version in Forth.
(but I think I know someone who might........)
Update
I have since found someone has done a C64 conversion with more colours and some other changes.
Adverts
If you want to play the original ZX81 version, that will run on a Minstrel 2 (with 8K BASIC) or a Minstrel 3. Those are available from my Tindie store:
- https://www.tindie.com/products/tynemouth/minstrel-2-with-keyboard-z80-based-zx80-kit/
- https://www.tindie.com/products/tynemouth/minstrel-3-with-keyboard-z80-based-zx81-kit/
If you want to try the PET version, I have some Mini PET 40/80 Internal boards as well.
Shipping - I can still ship worldwide
Currently shipping Royal Mail to the US is working. I can pay the 10% tariff upfront (as part of the postage), so that should be plain sailing and no problems with customs. We're all pawns in a petulant child's political games, but we've got to just keep going and try to make this stuff work.
Patreon
You can support me via Patreon, and get access to advance previews of posts like this and development logs on new projects like the Mini PET II and Mini VIC and other behind the scenes updates. This also includes access to my Patreon only Discord server for even more regular updates.