Sunday, 1 November 2026

Cosmic Snake - Writing Code Without Using Zero

In the previous post, I looked at Cosmic Snake for the VIC20, a game which was partly written in BASIC, and partly in assembler, with the code hidden in REM statements at the start of the program.

This is a technique used a lot on the ZX81, but rarely in Commodore BASIC as it has one major limitation - you cannot use zero.

If any of the code assembles to a binary $00, it will be seen as the end-of-line marker and the code after will be truncated.

That also means you cannot JSR, JMP, read or write to any addresses containing zero, (i.e. $9600, $1E00, $0042, etc.). There are ways around this, as we will see, but it complicates some things and leads to some convoluted code to avoid it.

If you have any variables or tables in this space, you also need to make sure they will never contain zero.

I guess this must be what it was like to write VMDII assembler for your VIC XX if you were a Roman.

Aiming For Zero

Here are three lines of code just to get A to $00, because you can't do LDA #$00.

Four bytes seems a bit unnecessary.

This is not a Z80, so you can't do XOR A, which would set A to zero.

If it not later 65C02, so we cannot use the nice STZ opcode which stores a zero.

It is going to need at least two bytes, which is what LDA #$00 would have been anyway.

There are many places in memory which will already contain $00.

It is defined that the start of BASIC memory should be zero, so $1000 would always be zero, you could read that

LDA $1000

The first line number is 0, so $00 $00 is stored at $1003 and $1004, you could read one of those

LDA $1003

But those are three bytes (still less that four), so to get down to two bytes needs a read operation from zero page.

There are various unused locations there that could be set to zero, POKE the address from BASIC, set it once to $01 and decrement it, or find one that would always be zero, for example:

  *0099     153      Input device (normally 0)

and then

LDA $99

Although giving that a name would be clearer

LDA ZP_ZERO

would assemble to two bytes, the same size as LDA #$00.

But it seems that none of those are used, instead we have lots of alternative workarounds.

Reverse Engineering

I find this an interesting challenge. Looking at the code as it currently is, there is a whole bunch of bytes. Some are code, some are data, some are BASIC line structure, and some are just padding.

I like working through and finding a variable and trying to work out what it is being used for. Where is is written to? when is it read from? what values will it contain?

The process was just a case of divide and conquer, breaking things down into functions and blocks and trying to work out what each one does.

As the code is a mixture of BASIC line structure, code, possible variables or tables and padding, I can't just run it through a disassembler as a whole.

Instead, I am using the disassembler in Vice.

I start at each REM statement line and look for valid code.

This section looks like a function.

The two bytes before are CLI, $58, the "X" character that fills the REM statements, so I think it is safe to assume that the function starts at $18C8.

The only jump, a BNE is to an address within the function, so I think it finishes at the RTS.

I cut and paste that section into Visual Studio Code, which I am using as an editor.

I don't need all the address and bytes on the left, so I can use the block select, which is quite handy to have in VS Code and is something I used to like about Notepad++.

Hold down SHIFT + ALT and you can select just that block that I need to remove, and then press TAB to set the code in it's normal location 4 characters from the left hand edge.

The *=$18C8 sets the address, so when the file is assembled, this code will overwrite what is currently at $18C8, which is a run of $58, the "X" padding characters. Overwriting the file like this means I don't need to worry about line formatting or padding, just blocks of code.

This is a local label, for local people. There is nothing for you here.

I am starting to split it up into sections, the first being the slightly convoluted way to make A=0.

As mentioned before, the BNE instruction branches back into this function, so I can replace that with a local label.

The - branches back to the nearest - label before the branch instruction, and a + will branch the next + label after the branch. You can do -- and ++ etc. to nest these on some assemblers, but I tend to switch to names at that point to avoid confusion, but for simple loops like this and "skip over the next few lines", they are ideal.

That just cleans things up a bit, my aim is to get rid of as many of the magic numbers as I can, and replace everything with named values or local labels where appropriate, etc.

Don't Keep Asking Me Why Reg

One of those is in the range $900x, which is the VIC chip.

It looks like it is writing A (which is zero) to $9009, which is the paddle Y register? Y Reg? Why?

Ah, but X starts at $01, so the instruction

STA $9009,X

Will actually write $00 to $9009 + 1, which is $900A. This is the register which controls the frequency of oscillator 1, which is more likely to be what we want here.

Then X is incremented, and checked against 6, if it is not 6, it loop back around.

The second time around, X is 2, so it writes to $900B, the frequency of oscillator 2.

Then $900C, the frequency of oscillator 3, then $900D, the frequency of the noise source.

The last time around the loop, it writes to $9009+5 ($900E), setting the VIC volume to 0.

That makes more sense, this is basically a "stop making sounds" function.

To make that clearer, rather than

STA VIC_PADDLE_Y, X

I have written that as

STA VIC_OSC1_FREQ-1,X

$900A-1 is $9009, so the code assembles to the same thing, but it is hopefully clearer what is intended rather than leaving it as just the number $9009 or changing it confusingly to VIC_PADDLE_Y.

Continuing on with the next section, and I can see in there are several calls to $18B6, which can now be updated.

There Has to be Another Way

It is interesting to note that there is no reason for this one-based confusion. It is used throughout the code, in many cases because it is just easier to avoid zero that way. But in this case, it is almost confusing for confusing sake, or maybe just a desire to keep everything one based rather than a mix of zero- and one-based?

The code actually starts by setting X to 0, and then sets it back to 1. It could have been left as zero, and then the function would read more logically.

Or, start with X as 5, and decrement until it gets to 0, we need it to keep going for that last loop, so you can change the branch instruction to BPL -, branch is the number is positive, which will be correct for 5 down to 0, but when it rolls around to $FF, the test will fail and it will move on to the RTS.

Going backwards is not always an option, but in this case, i would actually be better to start by setting the volume to 0 first.

You might also consider using X as an offset to the VIC base address $9000, and run the loop from $09 to $0E.

But the VIC base address is $9000, and that would assemble as $9D, $00, $90, which contains $00 and would cause the rest of this line to be truncated.

For now, I am leaving all the code as is, so I can continue to build a binary identical version.

Line 0

The first line is line 0, ironically you can use zero as a line number. This does not appear to start with valid code?

It seems to be rubbish until $1080 where it looks to be a valid function.

This fits with the BASIC code, which calls SYS 4224, which is $1080.

I worked that out to be a function that copies the data from $1006 onwards into RAM at $1D88, which is later set to be user defined graphics.

It is a little more than a straight copy.

It reads the data, byte by byte, and checks each one to see if it equals $01. If it does, it subtracts $01 to get the otherwise unobtainable $00 value. Presumably there was no need for any $01s in the character data.

You can see the results after the copy, the source first, then the destination, with all the $01 bytes changed to $00.

Fire!

Another short function that shed some light on things was this one.

>C:1150  22 91 60 58  58 58 58 58  58 58 58 58  00 5c 12 02   ".`XXXXXXXXX.\..

>C:1160  00 8f 02 02  58 58 ad 11  91 09 df c9  ff f0 05 a9   ....XX..........

This shows the end of line 1, with the row of XXX padding characters, then the $00 which indicates the end of line (the only time you can use a zero, other than in line numbers and the address of the next line).

$125C is set as the address of the next line, and $0002 as the line number.

There is then the $8F which is the REM token, then some code?

It does not start with valid code, two $02 (invalid op-codes, causing a CPU JAM), then two $58 padding characters, then what looks like a function follows at $1166.

This again ends with an RTS and appears self contains as the only branch, a BEQ will jump to the RTS line.

As before, I split that up, but left space for the two $02 bytes. I used another *= to skip over the padding bytes.

$9111 is the VIA#1 PORT A, and it looks to be masking off bit PA5, which is the joystick fire button.

If that is high, then the button is not pressed, so the function exits via the BEQ and the RTS.

If it does not equal $FF, then the pin must be low, so fire is pressed.

When this happens, it sets address $1162 to $01.

Hang on, that is the address of one of the $02 values above.

OK, so those are used as variables, I added V_ and R_ prefixes to try to track variables and routines (also T_ for tables and L_ for links), and a local label for the branch forward.

V_1162 starts as $02. Elsewhere in the code, in what I think is initialisation, it is set to $02.

The rest of this routine reads from an address which is the IO chip, and based on the bit is is checking, this is looking for fire being pressed. If it is pressed, it sets this flag variable to $01.

Elsewhere, it checks if the value is $01 and starts a process I worked out to be firing the missile.

Once it sets the missile in motion, it sets the value back to $02.

It also sets to back to $02 later during the movement of the missile.

There are no other references I can see to V_1162, so I think that is it's entire job.

From that, I can say that is a "Fire has been pressed" flag, and the other variable here V_1163 is "a missile has been launched" flag.

These are only ever set to $01 or $02, and are only checked for those value.

I think I can conclude the values are being used as

  • TRUE / ACTIVE = $01
  • FALSE / INACTIVE = $02

I am sure the programmer would have liked to use the more standard $00 for false and anything else for true, but of course we can't use $00 here, so the logic here is using $01 and $02.

There is another function buttoned up. I have left the literals for the mask values, I could have changed those to PA5_MASK or FIRE_MASK, but I think it is fine for now, I have added appropriate comments.

Turning the Tables

The third line number 2 started with a block of numbers before the valid code started.

Those were referenced by the following code. Registers A, X and Y were stored in $177E, $1780 and $1781 respectively, and were restored at the end of the function.

There was then a reference to $1778, but this was +X, so $1779,$177A etc.

I worked out this was the "update the score" function, and $1778 was a lookup table for scores.

The scores were of course stored plus one, but I thought it would be better to show those as 50+1 etc.

The temporary A,X and Y register locations were also named and the final byte was a variable used to store the number of points read from the table.

It would then loop around that many times, updating the score digits directly on the screen.

(why not load Y with the number of points and then DEY to avoid the compare, and why use JMP - when BNE - would have been shorter and also protected against infinite loops as it would eventually hit zero and not branch)

Writing to the screen

When I see JSR calls in the range of $B000 - FFFF, these are KERNAL routines, normally well defined and documented, so when I see those I can replace them with the function names.

Likewise, there are defined ares in zero page and $0200-$03FF that can be labelled appropriately to help understand what is going on.

This short function, followed by lots of padding, references two known memory locations.

From those, I could also understand the relevance of the values $07 and $39 in the code.

This is setting the current colour used by the kernal print routines to $07 (yellow) and using the print routine to print a character $39 to the screen.

This is one in the range which was copied over at the start of the code, and I had worked out was the snake graphic.

In order to use that function, there must be a call to set the location on the screen somewhere ...

Found it, a call to $FFF0.

It took me quite a while to work all that out because some of the references were to a variable at $1508, e.g.

LDX $1508

but others were as if $1508 was the start of a table

LDA $1508,X

I need to remember how the author is working here, it makes more sense when that is written as

LDA $1509-1,X

I worked out that $1508 was an index to the current portion of the snake being drawn, and there were several tables this indexed showing X and Y positions and I think direction (or possibly status).

With those defined, the code makes more sense.

This sets the X and Y location to be printed using the PLOT function.

Those are read from a table using an index (stored plus one to avoid containing zero, so the table address is -1)

And of course, the values are also stored plus one as they cannot contain zero.

(there really is a whole lot of work required to avoid using zero here)

But Wait, That's Cheating!

This function started by reading and writing to certain bytes in the $1Dxx region. This is where the user defined graphics are stored in this setup. Is it changing part of a character?

I thought this might have been related to the mushrooms being blasted away by fire, but that is handled by one of four different characters with different states of destruction.

I finally worked out that these were actually being used as variables.

Variables in real RAM!

Variables that can contain zero!!

But that is cheating!!!

If you are going to do that, why not put all the variables in there so we don't have to do this +1, -1 all the time.

Why not put the code there as well, save us all a headache?

I worked out these are being used as player X and Y locations. When the joystick is checked and the player needs to be, the new location is calculated and what is there is checked, using the KERNAL PLOT and the zero page CURSOR_POS and CURSOR_LINE_PTR functions, (which is quite a neat way of doing it)

That all seemed to make sense, following the code through until I hit a line that didn't make sense.

The code is reading in the port which contains several bits which are the joystick directions (3 directions plus fire, the 4th direction is on another port, because VIC20....)

If it detects the joystick is to the right, it increments the player Y position, to the left, decrements Y, etc.

When it comes to checking up, it should skip over the 

DEC NEW_PLAYER_X 

line if the joystick is not held up.

But rather than

BCS $10E9

Which would branch over the next line and continue, it does

BCS $10E7

This branches into the middle of the next instruction.

It just happens that $86 $1D represents a valid bit of code which does not break anything.

It unnecessarily writes X to address $1D, which is some temporary storage for strings in BASIC.

0019-0021 25-33 Stack for temporary strings

All I can say is the author was very lucky, that could easily have been invalid code and broken things, but by a fluke, it works out to be an innocuous instruction and it continues at $10E9 where it was meant to go.


I have not completed the whole disassembly, I will probably return to it at some point. It is an interesting process, and keeps my mind occupied anyway on days when I don't have the energy to do anything else.

I learned a few new tricks, but I suppose the main takeaway is, yes, you can store code in REM statements on a VIC20, but you really, REALLY, shouldn't.

I suppose you could rewrite this as "code at the end of the file" style, I wonder how much better / easier it would be..... No.....

Or even entirely in assembler ....

No.... Must not rewrite it ......

Adverts

All Tynemouth products are still available direct from me, and I can ship worldwide.

Including the limited edition 10th anniversary recreation of the very first Penultimate Cartridge:


International shipping is a bit complicated at the moment, so if you want to order from outside the UK, please contact me using the contact link at the top of this page.

See here for more information:

Sorry to have to do this, and I know this is going to affect sales, but please understand it is the only option I have right now.

Stop Press!

I am going to do another batch of Mini PET 40/80s with keyboard, with a few updates. 

These are pre-order now, shipping worldwide in November. £450 kit / £500 built.

Available exclusively from me, contact me using the link at the top of the page to get a price for shipping and any duties, based on your address.

The Mini PET 40/80 internal PET replacement boards are also available now, £350 kit / £375 built.

Again contact me if interested.


Patreon

If you enjoy posts like this, you can support me via Patreon, and get access to advance previews of blog posts, and exclusive posts. Paid Patreon supports will be getting the third part shortly, and maybe a forth part three will follow if there is any interest

You also get progress updates on new projects and other behind the scenes updates, as well as access to my Patreon only Discord server for even more regular updates, and to discuss your own projects.

Sunday, 25 October 2026

Cosmic Snake - Machine Code in REM statements on a VIC20

Cosmic Snake by Frank Lindsey is a bit of an unusual VIC20, not at least because it isn't a snake game, it is a not a bad Centipede (or is it Millipede ? or is it Caterpillar ?) style game.

In a recent video, TFW8b played all the games in the "Centipede Type Games" category on the Penultimate Cartridge

He did think it might be BASIC, so when it exited to BASIC at the end, he tried to list it.

The listing was full of "magic characters" and corrupted the display.

I was intrigued, so had a look at the listing.

It starts with some REM statements full of the aforementioned magic characters, this is line 9, the least magic of them all.

By line 100, it has returned to standard BASIC, including some SYS commands to call code in the 4000-500 range. That is where the BASIC program is on an unexpanded VIC20, starting at 4097 ($1001).

The BASIC code ran on until line 820.

What Is Going On?

Putting machine code in REM statements was a common trick used on the ZX81, but I can't remember seeing it used in Commodore BASIC before.

A simple example is the code to read the joystick port on a ZX81, as described in the post on the Minstrel Joystick Interface for RC2014 and Minstrel 4th

A REM statement is a place to add comments (or REMarks) to the code, it is purely there for readability and does not affect the game, other than making it slightly slower as the code has to be skipped by the BASIC interpreter.

It can be (ab)used as a place to store machine code.

You type a line, almost always at the very start of the program, as that location in memory is normally fixed. This line will contain a REM statement and sufficient characters for your machine code program (one character = one byte of code).

1 REM ......

You then POKE directly into memory where that line is stored, either by adding lines of code

10 POKE 16514,175

20 POKE 16515,71

etc.

or by directly typing in the POKE commands

POKE 16514,175

POKE 16515,71

etc.

Or if there is a lot of code, you can write a short loader program to get the user to type the bytes in and then poke them into RAM and then SAVE the program. Some of these loader programs ever computed a checksum to make sure you got it all correct.

Once you have entered the code, the REM statement now shows the magic characters, which are simple the character representations of the bytes of code.

Some of these are keywords, so you end up with things like the <= operator and the TAN keyword in the above example, both of which represented by a single byte.

In the ZX81 world, you then call that code with USR 16514 (or whatever is the address of the routine to call).

Onto the VIC20

There is a good reason why I have not seen this done in Commodore BASIC before, on the PET or VIC20.

Because of the structure of the BASIC program, you cannot have any zero bytes in your code. When the BASIC program is loaded, each line is parsed and if necessary, relinked, and when it finds a zero, it assumes it is the end of the line, so crops any code after that.

Just think about that for a moment. 

You cannot have any zero bytes in your code.

That means none of these type of instructions can be used, as they all assemble to code which contains a $00 byte.

LDA #$00

LDA $1400

JMP $1600

STA $9600,X

BRK

You have to be careful about variable and function locations to avoid any addresses ending in $00, adding padding or moving things around to avoid those locations. You also need to find other ways to end up with $00 in a register if you need it.

There are various other ways to get some assembler code on those machines, including placing the code into an area of RAM above the BASIC program, and saving them as a combined unit, so they get loaded together and the routines can be run by using the appropriate SYS 1234 style commands.

You can also store short sections of code in DATA statements at the end with code which are then READ and POKEd into a suitable area of RAM (e.g. the cassette buffer).

That is slow and inefficient, taking up to four bytes plus trimmings for each byte of code. This is normally only used for very short routines, such as this one from Tornado.

I was intrigued to see how the code in Cosmic Snake got around this, so started to reverse engineer it.

Code Layout

The code starts with the REM statements, of variable length, on line numbers 0, 1, 2, another line 2, 4, 5, 7, 8, 9, 10, yet another line 2 and finally line 12.

Line numbers don't actually have to be sequential, or unique, but it gets very confusing if they are not. See for example directory listings you get back from LOAD "$",8 on a Commodore disk drive.

Those are program listings, LOADed and LISTed like any other program.

After the REM statements, there is a short BASIC program with odd gaps, 100-125 in gaps of 5, then 200, then 300, 305 and then up to 700, 705, up to 720, then 800-820 also in 5s.

This is sprinkled with SYS calls, jumping into the code in the REM statements at various points.

The BASIC code is mostly doing things that can be a bit of a pain in assembler, such as the title page and high scores etc.

Storing Machine Code in REM Statements

The format of a line in Commodore BASIC is fairly simple:

  • 2 bytes with the address of the next line
  • 2 bytes for the line number
  • 1 or more bytes of program code, not including zero
  • 1 zero byte indicating the end of the line

Each of the lines fit into that format. I will show line 9 as that is one of the clearer ones.

>C:1601  73 16 09 00 8f 58

It starts at address $1601. The first two bytes are $73 and $16, meaning the next line starts at $1673 (the 6502 is little-endian, so the first byte is the lest significant, or the little end).

Next is the line number, $09 and $00 make $0009 or line 9.

Then the code starts, $8F is the BASIC token for REM. Whatever follows between the $8F and the next $00 will be skipped over by the BASIC interpreter.

There is a single padding character, $58 or "X", which is used throughout the program. I suspect the original code was written with lines like

9 REM XXXXXXXXXXXXXXXXXXXXXXX

and the code was POKEd into the memory, or assembled there using an assembly monitor.

Inside (line) Number 9

We are now at $1607, which is a place where the code calls this routine, using SYS 5639 (the decimal equivalent).

; $1607 - sys5639 - 

.C:1607  A9 01       LDA #$01

.C:1609  8D 07 15    STA $1507

.C:160c  8D 08 15    STA $1508

.C:160f  AE 08 15    LDX $1508

.C:1612  BD 26 15    LDA $1526,X

.C:1615  C9 01       CMP #$01

.C:1617  F0 06       BEQ $161F

.C:1619  EE 07 15    INC $1507

.C:161c  20 49 15    JSR $1549

.C:161f  EE 08 15    INC $1508

.C:1622  AD 08 15    LDA $1508

.C:1625  C9 06       CMP #$06

.C:1627  D0 E6       BNE $160F

.C:1629  60          RTS

The code completes with an RTS and control is returned to BASIC.

This is called from line 125:

125 geta$:sys5639:fork=1to10:sys6315:next:ifa$=""then125

This appears to be waiting for a key to be pressed. I think this is the title animation, which includes a "cosmic snake" moving around the title.

Quite a nice touch, especially for an unexpanded VIC20 game.

Directly after that routine is another padding character, another X, which shows up as a CLI in assembler.

.C:162a  58          CLI

(one of my few gripes about the 6502 is CLI - CLear Interrupt disable bit, in other words, enable interrupts which seems the wrong way around. SEI is SEt Interrupt disable is similarly backwards. Other than that, the 6502 is great and far better than any other processor I might have a rather large box full of ...)

Another routine follows at $162B, which is called from elsewhere in the code, not directly from BASIC.

; $162B

.C:162b  A2 01       LDX #$01

.C:162d  A9 01       LDA #$01

.C:162f  9D 08 15    STA $1508,X

...

.C:1665  A9 09       LDA #$09

.C:1667  8D 0D 17    STA $170D

.C:166a  60          RTS

After that, there are seven bytes of padding, again $58 / CLI / "X", presumably to allow the code to grow during development, or the code was originally longer but was shortened at some point.

.C:166b  58          CLI

.C:166c  58          CLI

.C:166d  58          CLI

.C:166e  58          CLI

.C:166f  58          CLI

.C:1670  58          CLI

.C:1671  58          CLI

And finally you are allowed a single $00 to indicate the end of the line (which the disassembler interprets as a BRK)

.C:1672  00          BRK

Line 10 follows durectly after at 1673

; $1673 -  line 10 remx etc.

>C:1673  72 17 0a 00  8f 58 

That continues up to line 12, and that links to line 100, which is actual BASIC code.

; $1964

; 100 print"{clr}":poke5697,6:sys4224:sys4248:sys5765

>C.1964  87 19 64 00  99 22 93 22  3a 97 35 36  39 37 2c 36

>C:1974  3a 9e 34 32  32 34 3a 9e  34 32 34 38  3a 9e 35 37

>C:1984  36 35 00

The numbers in the SYS commands are stored as characters ($9E $35 $37 $36 $35 at the end is SYS5765).

That means there would never be zeroes there, only $30 which is "0", not $00. It also explains why BASIC can be quite slow to parse these lines each time as there is a lot of converting 4 or 5 character strings into numbers.

Reverse Engineering

I have disassembled all the code and various blocks of non-code which is either padding or variables or character data. I have not carried out a full reverse engineering to work out what all the code does.

Ed: Well, not at the time I wrote this anyway ....

I did spot a few interesting things.

I found a few occurrences of things like:

.C:18c8  A2 01       LDX #$01

.C:18ca  CA          DEX

.C:18cb  8A          TXA

This loads X with $01, then decrements it to get $00, and then transfers it into the accumulator.

Normally this would have been just LDA #$00 but that would have been assembled as $A9 $00, and the $00 would have be detected as an end of line marker.

This is the End ...

When looking through the binary file in a hex editor (as you do ....) I was interested to see it did not end with three zeroes, as a pure BASIC program normally would.

The first zero is the end of line marker for the last line, the next would be the address of the next line, and $0000 indicates the end of the program.

Looking at the code, line 820 is the last line, and I found that ended earlier in the file, ending at $1C75.

But the file goes on for another 211 extra bytes at the end?

It doesn't seem to be valid code or text or anything, although the strings "$0000" and "BRK" indicate this could be left over memory from the assembler? It seems like there is every form of zero, as if breaking free from the previous limitations.

The files does contain 3,400 bytes exactly (in decimal), so maybe that was a save from the assembler / monitor program, rather than a standard BASIC save?

Looking through the code, I can't see any references to $1C75 - $1D49, so maybe it is just garbage at the end of the file as you sometimes get with a "save slightly more than the file size, just to be sure" option.

I tried removing them, and cropping the file so it finished at the end of the code as it should, and it seems to run fine with no difference, so I will ignore it.

Ironically, after all the work to get the zero-free code into REM statements as part of the BASIC program, the author has managed to demonstrate a more viable alternative of placing code into the area after the BASIC program, where you can use $00 without recrimination. (just be careful about where BASIC stores its variables and heap etc.)

Reconstructing the Code

I decided to try to recreate the program from BASIC and assembly sources, to see how it would work. 

Yes, I know I could have been down the roller disco with the cool kids, but instead I was at home reverse engineering a 45 year old computer game....

I now have a BASIC program that is the same size and matches from line 100 onwards, but with all the REM statements full of X's.

My God, it's full of stars ....

Now all I need to do is add in the assembler code, the character data and various bits of padding which I think are variables?

I can do that with ACME assembler as it allows me to insert a binary file (the PRG of the BASIC program) and also to overwrite bits of it by adding code with the .ORG directive setting the location within the file.

...

Well, that wasn't too bad.

I now have two source files.

  1. A BASIC program which contains the REM statements full of Xs and the rest of the BASIC code
  2. An assembler files containing all the reverse engineered code

I have done it this way so the assembler file contains just the assembler code, and does not need to recreate all the BASIC line framework.

The assembler file also has several sections which contain short runs of non-padding bytes. They don't make viable code as far as I can tell.

Some are referenced in the code, so they could be variables or data, or they could just be random padding (for example to an extra byte to move a function from $1600 to $1601 so it can be used in a JMP or JSR call)

As it stands, this builds from BASIC and ASM sources a binary identical file (other than the random data at the end, which does not affect the game or get referenced)

I have not commented most of the assembler, I would need to go through and work out how it all works.

The next step would be going through and turning the literal function and variables addresses into labels and trying to work out what they all do, and looking for system addresses like the VIC chip and known zero page addresses etc.

But that is a job for another day

Ed: Well, I wasn't planning to, but I have now almost completed a fully commented disassembly of the code, a full dissection of which will follow in a later blog post

Adverts

The VIC20 Penultimate Cartridge +3 DCR is available now from the Future Was 8 bit.


All Tynemouth products are still available direct from me, and I can ship worldwide.

Including the limited edition 10th anniversary recreation of the very first Penultimate Cartridge:


International shipping is a bit complicated at the moment, so if you want to order from outside the UK, please contact me using the contact link at the top of this page.

See here for more information:

Sorry to have to do this, and I know this is going to affect sales, but please understand it is the only option I have right now.

Stop Press!

I am going to do another batch of Mini PET 40/80s with keyboard, with a few updates. 

These are pre-order now, shipping worldwide in November. £450 kit / £500 built.

Available exclusively from me, contact me using the link at the top of the page to get a price for shipping and any duties, based on your address.

The Mini PET 40/80 internal PET replacement boards are also available now, £350 kit / £375 built.

Again contact me if interested.


Patreon

If you enjoy posts like this, you can support me via Patreon, and get access to advance previews of blog posts, and exclusive posts. Paid Patreon supports will be getting the third part shortly, and maybe a forth part three will follow if there is any interest

You also get progress updates on new projects and other behind the scenes updates, as well as access to my Patreon only Discord server for even more regular updates, and to discuss your own projects.