Skip to content

Back to Basics

Like many bedroom coders of the 80’s, My hacking exploits started from an early age, with a fascination of programs like Tomorrow’s world and tinkering around with my old electronics set, I discovered a knack, calmness and need to learn and experiment.

We couldn’t afford a computer when they started to become affordable and mainstream. That didn’t stop my Uncle taking me to local computer fairs or shops to tinker around on one. Though I didn’t own or have access to a computer, I would borrow books from the library or write down listings from computer magazines. These listings were inevitably Microsoft BASIC and meant to be widely compatible… I later found BASIC was anything but with each machine having subtle nuisances and dialects. Basically they didn’t work without modification though that’s a story for another day.

Probably one of the very first books I laid my hands on is shown below. Thankfully Usborne released the series as free PDF’s here: https://usborne.com/gb/books/computer-and-coding-books It may be fair to say that these books probably kickstarted many of our computing careers, though how many of you will admit it?

I would read these books and write programs on scraps of paper, following the rules and syntax of the language, unsure if they’d work, it was fun, exciting and I knew computers were more than a passing hobby for me.

My first home computer was the Commodore VIC20 sporting a whopping 3.5Kb of memory including on-screen colour! These were exciting times, I ignored the game that came with it, quickly progressed from BASIC to Assembly language programming. Assembly was just the start with the 6502 processor, since then I worked my way up through different processors, 6800 (Dragon 32), 68000 (Motorola Atari ST/Amiga), Z80/8085 through to common intel processors today.

Back then, and still even today, I’m rubbish at playing games. Don’t get me wrong, I love em, and I’ll use the excuse my reflexes are much slower than they were 40 years ago.

The only solution of course was to change the rules in my favour! It’s no different to the Kobayashi Maru where Captain Kirk changed the rules to win. I’d do the same for computer games. Back then it’s more challenging than it is today with modern emulators and various tool chains available. I’d have to peek memory, write the codes down and look them up in a table of op-codes. Tedious, boring, though you got to learn opcodes by number quickly.

Today’s blog I’m going to take you back to hacking on the VIC20 using the popular VICE Emulator with one of my favourite VIC20 Games “MegaVault‘ from Imagine Software. We’ll use some of the common coding patterns to target and locate injection code to cheat, finally wrapping it up in a loader that could be used with the original cassette if you have a real VIC-20. Sound fun? Let’s “Crack” on!

The Emulator

One of the most popular emulators for the Commodore Series of computers is VICE, the project is currently maintained at https://vice-emu.sourceforge.io

I’m using 3.6.1 GTK3 Version (24th January 2022), go ahead and install it on your device of choice, it’s available for Windows, MacOS, Android and other devices.

Commodore VIC20 Emulator Display

Familiarise yourself with loading and playing games within the emulator, there’s a wealth of software available including new releases from indiedevs over the last decade. The VIC-20 is a popular machine for various coding jams and game/demo creation contests. If you really want to get into development, without too much pain, then I recommend Turbo Rascal as a development suite found: https://lemonspawn.com/turbo-rascal-syntax-error-expected-but-begin/downloads/ It really is a nifty cross platform tool with extremely optimised libraries!

The Game

Next up, the game! Mega Vault from Imagine 1984. I’d forgotten about this little gem until it was released on TheVIC20 from RGL https://retrogames.biz/products/thevic20/

MegaVault – 1984 – Imagine Ltd

We take splash screens for granted these days, the VIC20 didn’t really lend itself to this, it didn’t have a writable graphics screen per-se, the only way around it was to redefine the ASCII Character set, and create a screen with text. Yeah that doesn’t sound right, but trust me, It might make sense later when I show the screen layout as text.

MegaVault is a simple idea, move your loveable beat bopping character to collect the food, find the key and release Fred, avoiding lasers, guards and moving objects that will try and thwart your progress.

Completed without Cheating, however I’ve edited out the failed attempts!

Back in 1984, the game was a struggle needing a lot of practice to get to the next vault. I’ve played this again on the real hardware, and I suggest the real difference between real hardware and the emulator is how responsive modern day keyboards compared to the ones back then.

We want to complete this game, if only I had infinite lives!

Quick Start

I know, times money and all that, you don’t want to read a boring old article, and that’s ok, we have you covered. We’ve created a video, mistakes and all showing how to crack the game and create the loader old school stylee for magazine type-ins as we would have done back in the 80’s.

Part 1 – Finding the Cheats to MegaVault
Part 2 – Tying it all up and creating a loader

First Things First -Reconnaissance

You’ll need to source your own copy of MegaVault if you don’t have it already, Google is your friend! Load it into the emulator and make sure your TAP image works correctly. You’re looking for an uncracked version as we’ll be using the original cassette data.

MegaVault starts off with three lives, fairly typical of games back then, though the more astute will realise you actually have four. There’s an “Air Bar” on the top right side of the game board and progress on the right.

Typically game developers in the early days followed a pattern, or prescription, we’ll focus on lives.

The Monitor – Finding Lives

With modern day emulation, VICE includes a built in monitor with plenty of commands to help, making this task much easier than it was back in the 80’s.

If you’re unfamiliar coding in 6502 or need a refresher like me who hasn’t done this in nearly 40 years, this will come in handy: http://www.1000bit.it/support/manuali/commodore/vic20/vic-20_machine_code.pdf

First we need to find the equivalent piece of code that sets the number of lives

LET LIVES = 3

There’s a number of ways to achieve this in 6502, the most common pattern are the assembler instructions

LDA #$03
STA

Notice I’ve left the STA Instruction incomplete. Those familiar with 6502 will know there’s a number of methods of addressing memory, i.e. Zero Page, Indexed, Indexed Indirect, Indirect Indexed etc. Probably explains why coders who’ve developed at this level of assembler are comfortable using Pointers in languages like C/C++.

Open up the Monitor in VICE using the File Menu and Activate Monitor, Or use the shortcut key.

File->Activate Monitor

You’ll find a nice command line interface

Monitor CLI

The Address where the monitor breaks into the program will vary, it won’t necessarily be the same each time you enter the monitor tool. Help is available, type help at the prompt. If you want to know more, check out this resource: https://vice-emu.sourceforge.io/vice_12.html#SEC291

Thankfully the monitor includes a “Search” feature, to look for sequence of bytes.
The OP-CODE for LDA #$03 is A9 03

Since we’re playing this on an unexpanded VIC-20, we’re only interested in the first few pages of memory.

This is a useful link to the memory map of a VIC20: http://www.zimmers.net/anonftp/pub/cbm/maps/Vic20.MemoryMap.txt

The abridged version is that we’re using address range $0000 – $03FF and $1000-$1FFF, theirs additional addresses for colour memory blocks, but code will reside here.

Zero Page : $0000 – $00FF is a special place for storing values in memory for fast access, the Kernel uses a number of these.

Page One: $0100 – $01FF is reserved for the Stack Pointer

Page Two: $0200 – $02FF is reserved for BASIC and Floating Point conversions

Page Three: $0300 – $03FF is reserved for cassette/disk buffer and other Kernel usage.

BASIC and free code starts at $1000, Screen Memory Starts at $1E00

Of course the rules change on expanded VIC-20s, we certainly liked to complicate things back then, but we’ll cover that off at a later date.

If you need a refresher on 6502 OP-Codes, these can be found here: http://www.6502.org/tutorials/6502opcodes.html though there are of course many other interesting references available using Google.

The storing of the byte/lives could be STA $xxyy STA ($xxyy) STA $zz STA $xxyy,x etc depending on the memory access method used. So let’s try searching for the sequence $A9 $03 (LDA #$03) using the HUNT Command, we could end up with no hits or many hits.

(C:$106b) hunt $100 $1e00 $A9 03
1805
18cc
(C:$106b) 

Wow, only two hits! That was unexpected and hoping this makes the process easier. Let’s have a look at the code around these two addresses $1805 and $18cc

Code can be disassembled using the d command, along with the memory address. I would recommend starting at least 10 bytes behind the matched address just to have a nose around, and making sure you’re not hitting the middle of a data sequence. I.e. not a false positive mid other instruction.

(C:$1926) d $17f0
.C:17f0 A2 0A       LDX #$0A
.C:17f2 A9 30       LDA #$30
.C:17f4 9D E3 1F    STA $1FE3,X
.C:17f7 CA          DEX
.C:17f8 D0 FA       BNE $17F4
.C:17fa A9 2B       LDA #$2B
.C:17fc 8D EA 1F    STA $1FEA
.C:17ff 8D EB 1F     STA $1FEB
.C:1802 20 42 17    JSR $1742
.C:1805 A9 03       LDA #$03
.C:1807 85 0A       STA $0A
.C:1809 4C 7E 15    JMP $157E
(C:$184e) d $18c0
.C:18c0 60          RTS
.C:18c1 15 20       ORA $20,X
.C:18c3 BA          TSX
.C:18c4 15 20       ORA $20,X
.C:18c6 8C 16 A9    STY $A916
.C:18c9 0C 85 B1    NOOP $B185
.C:18cc A9 03       LDA #$03
.C:18ce 85 0A       STA $0A

This is very interesting, both seem to match the criteria for number of lives and both are being stored in a Zero Page Address $0A.

To see if we have a match lets modify the code at both addresses using the Assemble command. Note I couldn’t find an option to poke memory in this monitor.

To see which address is the better option we’ll use two new life values 5 and 8. If the lives change to either 5 or 8 we know what controls the number of lives.

(C:$1928) a $1805
.1805 LDA #$05
.1807
(C:$1807) a $18cc
.18cc LDA #$08
.18ce
(C:$18ce) d $1805 $1807
.C:1805 A9 05 LDA #$05
.C:1807 85 0A STA $0A
(C:$1809) d $18cc $18d0
.C:18cc A9 08 LDA #$08
.C:18ce 85 0A STA $0A
.C:18d0 A9 01 LDA #$01
(C:$18d2) x

Now continue execution of the game by typing x at the CLI and start a new game.

Notice something strange? Yep, we have more lives than we started with, plus the high score appears to be corrupt. You’ll find you have 8 lives. Bingo! we now know that modifying the number of lives at address $18cd (note the actual value not the op-code) will change the number of lives the game starts with.

That’s amateur stuff though, We can do better! How does infinite lives sound? Much better? Great…

We know that Lives are stored at Zero Page Address $0A we need to find a byte pattern that decrements this counter. Again, 6502 has numerous methods to address and manipulate memory, we’ll go with the easiest version first and work our way up.

Remember the HUNT command? We’re going to do the same but look for the instruction DEC $0A which is $C6 $0A

(C:$2490) hunt $100 $1e00 $c6 $0a
0123
(C:$2490)

Wait… What? A hit already? at Address $123? This is interesting, because the VIC-20 Stack is contained in Page 1 ($100-$1FF), lets take a look at the code in that memory location.

(C:$2490) d $11d
.C:011d 4C FA 1F JMP $1FFA
.C:0120 20 42 17 JSR $1742
.C:0123 C6 0A    DEC $0A
.C:0125 F0 03    BEQ $012A
.C:0127 20 7E 15 JSR $157E
.C:012a 20 D1 10 JSR $10D1
.C:012d 20 D1 10 JSR $10D1
.C:0130 A9 0C    LDA #$0C
.C:0132 85 B1    STA $B1

This definitely looks like a good code candidate, we’ll return the code back to 3 lives and modify the code at $123, ordinarily I would NOP (No Operation) the piece of code out, however notice the BEQ (Branch if Equal) opcode? This means that after the decrement command if the result is Zero then change execution for what will be the end of game routines. You can approach this problem in one of two ways.

Option One, Use the ORA $0A instruction. This works since lives will always be a positive number leaving the remaining code intact.

(C:$018d) a $123
.0123 ora $0a
.0125
(C:$0125) d $123
.C:0123 05 0A ORA $0A
.C:0125 F0 03 BEQ $012A
.C:0127 20 7E 15 JSR $157E

Or in this instance we could choose to use

(C:$018d) a $123
.0123  NOP
.0124  NOP
.0125  NOP
.0126  NOP
.0127  
(C:$0127) d $123
.C:0123 EA NOP
.C:0124 EA NOP
.C:0125 EA NOP
.C:0126 EA NOP
.C:0127 20 7E 15 JSR $157E
.C:012a 20 D1 10 JSR $10D1

Exiting back to the game and now you find you have infinite lives! How cool is that?

You’ve now successfully hacked MegaVault for the Commodore VIC20 and it wasn’t that difficult! We could leave it at that and play the game…

The choice of using ORA vs NOP depends on the subsequent code to be executed, bot are valid for this scenario, but maybe not for the next. I’m going with ORA since we only need to poke one byte of memory instead of four. It’s a more elegant solution.

But wait, there’s more…

We also wanted to give ourselves an infinite air supply! This one is slightly trickier to achieve, but still doable with modern emulator toolkits.

We don’t really know at this point how many units the Air Bar represents, all we know is that the graphics will change as our air depletes. This is where breakpoints on memory change are extremely useful!

We’ll use another useful feature of the monitor program called sc or screen

You’ll see what looks like a bunch of gobbledy gook… The VIC20 doesn’t have a true graphics screen per-se, instead user defined characters make up the screen image.

The screen address starts at $1E00 and our Air Bar happens to be the first row. We can deduce that the SPACE Graphic is represented by ‘(‘ weird as it looks, and we have a little air missing from the bar so an education guess will be that the ‘ttttttttttts‘ represents air and the S is the smidge off the top, working across we know that’s the 12th character, which means a memory address of ?

(C:$0188) sc
Displaying 22x23 screen at $1e00:
1e00  )tttttttttttt*)pppppp*
1e16  hiiiiiiiiiiiiiiiiiiiij
1e2c  ouv!        bcbbbcb  k
1e42  owx!@     +         +k
1e58  oyz!        bcbbbcb  k
1e6e  o___________________ k
1e84  o +++c+++c++c+++c+++ k
1e9a  o                    k
1eb0  o +                + k
1ec6  o +++c+++c++c+++c+++ k
1edc  o ___________________k
1ef2  o  d     d    c   c  k
1f08  o          + aa b aa k
1f1e  o          +cac b cack
1f34  o          +         k
1f4a  o          + bb , bb k
1f60  o          +         k
1f76  o          +cac b cack
1f8c  o          + aa b aa k
1fa2  o  e     e +  c   c  k
1fb8  nmmmmmmmmmmmmmmmmmmmml
1fce  ++#$++++%&++++++++'(++
1fe4  000000++01+"""++000000
(C:$0188)

If you calculated correctly we’re looking around address $1E0C, how does this help us? Remember I said we could add breakpoints when memory changes? Well we’re going to use this feature to find where our air is depleted.

(C:$0188) break store $1e0c
WATCH: 1 C:$1e0c (Stop on store)
(C:$0188) X

There’s more we can do with breakpoints, feel free to experiment with them. For now, we’ll continue the game and wait for the memory to change at the address. Of course you may need to restart the game, lose a life or work out the correct screen address of the next air change.

If you set the breakpoint correctly, it will either break when initialising the level screen, or when it’s decrementing the air counter.

#1 (Stop on store 1e0c)  262/$106,  67/$43
.C:1199  DE 00 1E    DEC $1E00,X    - A:FE X:0C Y:1E SP:f0 ..-..I..  337903125
(C:$119c) 

If the game was setting up the initial start of level, just continue with X and wait until you hit the breakpoint at the address shown above.

(C:$119c) d $1190
.C:1190 B4 D0     LDY $D0,X
.C:1192 18        CLC
.C:1193 A9 FE     LDA #$FE
.C:1195 85 B4     STA $B4
.C:1197 A6 B1     LDX $B1
.C:1199 DE 00 1E  DEC $1E00,X
.C:119c BD 00 1E  LDA $1E00,X
.C:119f C9 10     CMP #$10
.C:11a1 D0 08     BNE $11AB
.C:11a3 C6 B1     DEC $B1
.C:11a5 D0 04     BNE $11AB

Looking at the code, we see an absolute indexed write to the screen $1E00,X by Decrementing the value, then loaded into the accumulator for comparison before deciding the next course of action.

You could simply NOP out that DEC Instruction with Three NOP’s

(C:$11e2) a $1199
.1199 nop
.119a nop
.119b nop
.119c
(C:$119c) d $1199
.C:1199 EA          NOP
.C:119a EA          NOP
.C:119b EA          NOP
.C:119c BD 00 1E    LDA $1E00,X

This would give you infinite air. Then again, could we reduce the number of pokes to just one instead of three? Yes we can! Look a little further back in the code

(C:$153c) d $1180
.C:1180 9D 9D 4F STA $4F9D,X
.C:1183 56 45 LSR $45,X
.C:1185 52 JAM
.C:1186 00 BRK
.C:1187 00 BRK
.C:1188 00 BRK
.C:1189 A5 B4 LDA $B4
.C:118b C9 FF CMP #$FF
.C:118d F0 1C BEQ $11AB
.C:118f C6 B4 DEC $B4
.C:1191 D0 18 BNE $11AB
.C:1193 A9 FE LDA #$FE
.C:1195 85 B4 STA $B4
.C:1197 A6 B1 LDX $B1
.C:1199 DE 00 1E DEC $1E00,X
.C:119c BD 00 1E LDA $1E00,X

We have some probable data, a few BRK statements and then the start of subroutine code dealing with air. Again we find a DEC instruction DEC $B4 that will decide if the code will branch if not equal to zero, or if zero will continue to update the screen and reduce the visual representation. We can use the same trick for infinite lives ORA $B4 or Simply use two NOP instructions since the Zero flag is clearly not set due to the previous instruction BEQ. Sounds complicated, so let’s stick with the ORA $B4 instruction.

(C:$11e2) a $118f
.118f ORA $b4
.1191
(C:$1191) d $118f $1194
.C:118f 05 B4 ORA $B4
.C:1191 D0 18 BNE $11AB
.C:1193 A9 FE LDA #$FE
(C:$1195) X

And hey presto! You’ve now added infinite air to the game, giving you all the time needed to complete the levels.

Fast Forward

I just want to get to the cheat! And that’s what we’re here for. In your emulator you would (In old money) do the equivalent of

Number of Lives

POKE 6349,Lives

However anything more than 5 will result in strange results or even game crashes.

Infinite Lives

POKE 291,5

Infinite Air

POKE 4495,5

Though you’re more likely to break into the program using the inbuilt monitor, this will be much easier, type the following exactly including the extra blank lines, take out the cheats you don’t need.

; Set Number of Lives to 5	
a $18cc LDA #$05

; Give Infinite Lives
a $123 ORA $0A

; Give Infinite Air
a $118f ORA $B4

x

The easiest way now would be to save a snapshot of the game in the emulator with the cheat enabled and there’s no additional work in the future.

What’s next?

We could finish here, job done, we’ve found some cheats and we can complete the game at our own leisure. If you’re feeling really adventurous, you could trace when the routine to decrement lives is called to find out how collision detection works and turn that off. The game wouldn’t be half as fun, but the challenge is great with the knowledge you’ve gained with using breakpoints.

I did say we’re going to create an old school loader/type-in for the purists, and we’ll cover that off in the next article!

Hope you enjoyed this

If you’ve enjoyed reading this article and would like to buy me coffee, who am I to say no, caffeine is what fuels me. You can make a donation here. Yep I hate begging to, especially in these uncertain times.

Choose an amount

£1.00
£3.00
£5.00

Or enter a custom amount

£

Your contribution is appreciated.

Donate

1 Comment »

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: