31 years later, here is the first ever file-based version of Conan, compressed to the point that it fits on one side of a floppy disk!

The retail version of the Apple II version of Conan came on two sides of a single disk. Recently, there was a request to make a ProDOS version, so that it could be played from a hard disk on real hardware, so I set to work.

To convert Conan to files, first we have to extract all of the data.


The initial reads look like this:

t00s00...t00s09 to B600...BF00

This is the RWTS. It's not interesting, we can discard it.

t00s0A...t00s0F to B000...B500

This is the music that is played during the credits.

t01s00 to 0200
t01s01 to 0300

This is the disk driver. We will replace this.

t0As00...t0As0F to 6000...6F00

This is the compressed title screen with the trees, and its mask screen.

t0Bs00...t0Fs0F to 6000...AF00
t04s00 to 1000

This is the credits code.

Now, t00s00 is the boot sector. It is read to $B600. Nothing strange about that - the standard DOS boot sector does the same thing, and Conan's RWTS is the one from DOS. However, during the credits display, we reach this point:

$89BD: JMP $B6F0

That one is strange. At $B6F0, we see this:

$B6F0: LDA #$01
$B6F2: STA $8FF9
$B6F5: STA $06
$B6F7: JSR $AE00
$B6FA: JMP $B000

Hmm, looks like someone forgot to initialise something, and had to cram it in later. That's an essential routine to update the screen and, most importantly, to make the music play. If you let the music finish playing, then Conan will ride his horse along the bottom of the screen. The most commonly-distributed crack doesn't play the music, because frankly, it's not very good, and the crackers liked the riding Conan better. We must keep it, though, because it's there. Continuing...

t03s00...t03s0f to a700...b600
t04s01...t04s0C to 0400...0F00
t05s00...t05s0F to 1000...1F00
t06s00...t09s0F to 4000...7F00

This is the game engine. At this point, we see the prompt to turn over the disk. Not much on side A.

t00s01...t00s07 to 7600...7C00

This is the compressed title screen with the fountain and the castle.

t00s08...t00s0B to 7500...7800
t00s0C to 7900
t00s0D...t02s0E to 7A00...9B00
t02s0F...t03s0A to A000...AB00
t03s0B...t03s0C to AC00...AD00

This is the data for the intro, the animation of Conan jumping the fountain and entering the castle. However, before it starts, we see this:

$1094: JSR $B731

Ah, this is the copy-protection routine that triggers before you get to play the game. It's the one that's designed to embarass any cracker who managed to copy the disk but didn't try to play it. The protection routine performs a dummy read of t10s0d to AE00 (it's identical to the one that was loaded earlier), and then attempts to find two sectors with identical prologue information, followed by a different byte to distinguish between them. If the two sectors are found, then control branches to $A000. So by replacing the $B731 with $A000, we have a more elegant bypass than overwriting the code at $B731.

t03s0D...t04s04 to 7500...7C00

This is the compressed level 1 screen.

t04s05 to 7500
t04s06 to 7900
t04s07...t04s0E to 7A00...8100
t04s0F...t05s07 to A000...A800
t05s08...t05s09 to AC00...AD00

This is the level 1 code.

I'm sure that everyone knows about the extra life that's hidden in the tree. Here's the code that's responsible for it:

$A008: LDX $7902 ;zero until bat is killed
$A00B: BNE $A016
$A013: JSR $A1B0 ;unreachable if bat is killed
$A1B0: LDX $0300
$A1B3: CPX #$7A ;X position, far to the right of the screen, specifically inside the tree
$A1B5: BCC $A1D1
$A1B7: LDX $0309 ;non-zero if starting to climb ladder
$A1BA: BEQ $A1D1
$A1C1: INC $034B ;extra life
$A1C9: LDX #$60 ;RTS
$A1CB: STX $A1B0 ;disable routine

Back to the game.

t05s0A...t06s03 to 7500...7E00

This is the compressed level 2 screen.

t06s04 to 7500
t06s05 to 7900
t06s06...t07s05 to 7A00...8900
t07s06...t07s0B to a000...A500
t07s0c...t07s0D to AC00...AD00

This is the level 2 code.

t07s0E...t08s06 to 7500...7D00

This is the compressed level 3 screen.

t08s07...t08s08 to 7500...7600
t08s09 to 7900
t08s0A...t0As02 to 7A00...9200
t0As03...t0As0D to A000...AA00
t0As0E...t0As0F to AC00...AD00

This is the level 3 code.

t0Bs00...t0Bs08 to 7500...7D00

This is the compressed level 4 screen.

t0Bs09...t0Bs0A to 7500...7600
t0Bs0B to 7900
t0Bs0C...t0Ds07 to 7A00...9500
t0Ds08...t0Es03 to A000...AB00
t0Es04...t0Es05 to AC00...AD00

This is the level 4 code.

t0Es06...t0Es0F to 7500...7E00

This is the compressed level 5 screen.

t0Fs00...t0Fs01 to 7500...7600
t0Fs02 to 7900
t0Fs03...t11s00 to 7A00...9700
t11s01...t11s0C to A000...AB00
t11s0D...t11s0E to AC00...AD00

This is the level 5 code.

t11s0F...t12s08 to 7500...7E00

This is the compressed level 6 screen.

t12s09...t12s0B to 7500...7700
t12s0C to 7900
t12s0D...t14s07 to 7A00...9400
t14s08...t15s03 to A000...AB00
t15s04...t15s05 to AC00...AD00

This is the level 6 code.


There is a fatal bug in the code for level 6. If the chandelier hits the machine at the exact moment that the first frame of the electric arc is being drawn, the code accesses a bad pointer and overwrites memory until the game crashes. The authors were clearly aware of the issue, and placed a check in the one other place where it can occur, but missed this one. The fix requires two bytes, but finding more than one byte requires some fairly extensive surgery.

t15s06...t16s01 to 7500...8000

This is the compressed level 7 screen.

t16s02...t16s05 to 7500...7800
t16s06 to 7900
t16s07...t18s0C to 7A00...9F00
t18s0D...t19s08 to A000...AB00
t19s09...t19s0A to AC00...AD00

This is the level 7 code.

t22s00...t220A to 9F00...A900

This is the bird animation.

t19s0B...t1As05 to 7500...7F00

This is the compressed "level 8" (epilogue) screen.

t1As06 to 7500
t1As07 to 7900
t1As08...t1As0A to 7A00...7C00

t1As0B to A000
t1As0C...t1As0D to AC00...AD00

This is the "bonus" code.


It looks like we're done, and we stop at track #$1A, sector #$0D, but wait! Since I had a lot of practice at the game nearly 30 years ago, I remember all of the safe places to stand, and I can complete it without dying. I almost forgot about the "death" screens.

Here's how that works:

$1C7C: JSR $1EA8 ;rand
$1C7F: AND #$03
$1C81: ASL
$1C82: ASL ;multiply by 4
$1C83: STA $B7ED ;sector (4 sectors per graphic, 4 graphics per track)
$1C86: LDA $034C ;level, 1-based
$1C89: CLC
$1C8A: ADC #$1A ;base track (#$1B)
$1C8C: STA $B7EC ;track

The routine that is called reads from the current sector until the end of the track. It means that the graphics load faster, the closer they are to the end of the track, and lots of sectors are read for no reason. It uses tracks #$1B-#$21 entirely. Surprise. The side B is really full.

When looking at the read patterns, we can see one thing immediately: they are really not efficient. Rather than having one file per read, we can consolidate some of the reads to reduce significantly the number of files.

In any case, the first file is the music and the title screen. We load these, and display the title, while loading the credits code, which is the second file.

As for the music hack, we can copy just the required bytes to their required location to make that work.

Since the "flip disk" prompt won't be needed anymore, we'll have to run the score display code manually, but that's not a big problem.

Then it's time for the game engine, which is our third file. It contains our first read consolidation, and some significant trimming. Recall this:

t04s01...t04s0C to 0400...0F00
t05s00...t05s0F to 1000...1F00

We can combine those into a single block of data, 0400...1F00, but the first text screen is entirely blank and never displayed. We can discard that, so we have 0800...1F00.

Next, we have

t06s00...t09s0F to 4000...7F00

which is followed immediately by

t00s01...t00s07 to 7500...7C00

from side B, which is the compressed title screen, and our fourth file. So what's with the ...7F00 in the first case? Nothing, just a legacy of the read-to-end-of-track routine, for someone thinking that some of the early 7xxx was needed. So, it's just junk that we can discard. We can reduce it to 4000...6F00, which is nice.

Now we're dealing with side B, and this monstrosity:

t00s08...t00s0B to 7500...7800
t00s0C to 7900
t00s0D...t02s0E to 7A00...9B00
t02s0F...t03s0A to A000...AB00
t03s0B...t03s0C to AC00...AD00

which is the intro, and our fifth file. It can be consolidated into this: 7500...9B00 and A000...AD00.

We've reached the level data. Each level consists of three files - the compressed level screen, the code, and the "death" screens. Level 7 is one exception to that, because of the additional bird animation; and level 8 is the other exception to that, because there are no "death" screens.

That brings us to 29 files, and quite a lot of data. Let's see about compressing it.

The problem with compressing the data is that the decompression cannot be done in-place, because the last few bytes of compressed data will be overwritten by the uncompressed data, before they can be fetched and decoded. To solve that, we have to find a region that is large enough to hold the last few bytes of compressed data along with the entire decompressed data.

Coming back to this:

t06s00...t09s0F to 4000...7F00

which we reduced to 4000...6F00. It turns out that we can reduce it further, to 4000...6C00. This is enormously helpful, because it leaves us plenty of room to unpack without overwriting ourselves. We can load the level data to $6D00 and there's no problem.

But what about the 4000...6C00 region itself, or the 6000...6F00 of the title screen, or the 6000...AF00 of the credits code? We solve that with two powerful words: screen holes. By simply shifting the compressed data to $3FF8 or $5FF8 respectively, the dispayed graphics screen will not show any corruption because the last eight bytes of the memory that holds the last line in particular (and all of other lines, in general) are not visible.

With that problem solved, we can compress the data to the point that it fits on a single side of a floppy disk. Imagine what other levels the authors could have included with a whole other side of the disk...

Copyright (c) 2015 Peter Ferrie
All rights reserved

This site is hosted by

Free web hostingWeb hosting