MASQUERADE / 2-drive edition
by Raven / The C700 Club / August 2011
Introduction
------------
The purpose of this exercise is to
patch an already-cracked copy of
MASQUERADE to read Side B from drive 2
so the entire game can be virtualized,
launched by DOS 3.3 Launcher, and
played from a ProDOS hard drive.
We will need:
- disk images of MASQUERADE sides A
and B from
ftp://ftp.apple.asimov.net/images/
games/adventure/masquerade/
- one blank DOS 3.3-formatted disk or
disk image
- a ProDOS hard drive with DOS 3.3
Launcher and DOS 3.3 Copier programs
- Copy ][+ 5.5
- an Apple emulator, or a real Apple ][
with two disk drives and the ability to
transfer disk images to it. We will not
cover the process of transferring disk
images to a real Apple ][. Learn how
at http://adtpro.sourceforge.net/
First Steps
-----------
With MASQUERADE Side A in drive 1, we
try to image it with DOS 3.3 Copier
to an image file called MASQ1.
Launch MASQ1 with DOS 3.3 Launcher.
(Refer to the DOS 3.3 Launcher
documentation on how to set up ProSel
or other programs to launch DOS 3.3
Launcher with the disk image file as a
"startup program.") It fails almost
immediately, stopping (as most failed
DOS 3.3 Launcher images do) while
loading the DOS from tracks 0-2.
Delete MASQ1; it was a failed
experiment.
We launch Copy ][+ to assess the
situation. The MASQUERADE Side A disk
appears to have a normal disk catalog
on track $11, with a stub HELLO
program and a 177-sector file named
MASQUERADE. (That's too big to be a
normal file, but we'll tackle that
later.) Viewing the track/sector map
on the disk shows nothing unusual.
Tracks 0-2 are taken by DOS, track $11
by the disk catalog, and the rest by
the HELLO and MASQUERADE files. No
sectors unaccounted for. A "Catalog w/
File Lengths" reveals that the
MASQUERADE file gets loaded at address
$0804. We will use this information
later.
At this point, it seems safe to copy
the 177-sector MASQUERADE file to a
new disk with standard DOS 3.3. Booting
that disk, we can make a quick HELLO
program to autorun MASQUERADE.
]10 PRINT CHR$(4);"BRUN MASQUERADE"
]SAVE HELLO
]PR#6
Indeed, MASQUERADE loads, displays
a text-based crack screen, displays
some intro text, and asks us to flip
to side B. Inserting the MASQUERADE
Side B and pressing RETURN confirms
that this is a working copy of the
game.
Put away the original MASQUERADE Side A
disk; we don't need it anymore.
Next, we image this reauthored
MASQUERADE Side A disk with DOS 3.3
Copier (call it MASQ2), then launch
MASQ2 with DOS 3.3 Launcher. This
time, MASQUERADE loads successfully,
displays the text-based crack screen,
displays the intro text, and asks us
to flip to side B.
How do we flip to side B of a virtual
disk image?
Delete MASQ2; it's served its purpose
(confirming that we can get Side A to
boot under DOS 3.3 Launcher), but it's
useless without a way to combine it
with Side B.
Live By The Sword, Die By The Launcher
--------------------------------------
DOS 3.3 Launcher is an amazing hack.
It can run many file-based DOS 3.3
games under ProDOS without any
modification, and it can virtualize
and "boot" entire floppy disks. It has
two major limitations:
1. Since it "boots" disk images by
patching the RWTS, it only works with
a limited number of DOS formats. We've
had success with standard DOS 3.3 and
some flavors of ProntoDOS. Some games
that ship without DOS but use a
standard RWTS (cracked versions of
Conan and Donkey Kong, for example)
will also work.
2. It has no UI, and therefore it
provides no way to "flip to Side B"
after a virtual disk image is booted.
We worked around the first limitation
by reauthoring MASQUERADE Side A on a
standard DOS 3.3 disk. We are now faced
with the second limitation: the game is
asking us to insert Side B.
There is a possible solution to this
problem. DOS 3.3 Launcher supports
combined disk images of two disks in
one file. It acts as if the first disk
is always in drive 1 and the second
disk is always in drive 2. MASQUERADE
never uses Side A after it asks us to
insert Side B. Once we flip to Side B,
it never asks us to flip it back. What
if we could patch the game at the point
when side A was fully loaded, so that
from that point on it looked for Side B
in drive 2 instead of drive 1? Then we
could create a combined disk image that
contained both sides of the disk (Side
A in drive 1 and Side B in drive 2),
and we could launch that combined disk
image with DOS 3.3 Launcher from a
ProDOS hard drive.
Tracing The Quickloader
-----------------------
With this goal in mind, we return our
attention to MASQUERADE Side A. As we
have learned, the entire cracked game
is contained in a single 177-sector
file. Let's boot the disk, break out of
the HELLO program, load the game
manually, and poke around.
]PR#6
<ctrl-C>
]BLOAD MASQUERADE
That returns suspiciously quickly, and
with good reason. This file uses a
Quickloader -- a program-within-a-
program whose only purpose is to load
the rest of the program into memory.
In other words, we don't have
MASQUERADE in memory yet; we need to
let the Quickloader do its job (loading
the game) and then break before it
transfers control to the game itself.
The Quickloader loads at $0804. (We
discovered this earlier using the
"Catalog w/File Lengths" feature of
Copy ][+.)
]CALL -151
*804L
0804- 20 58 FC JSR $FC58
0807- A9 0A LDA #$0A
0809- 20 5B FB JSR $FB5B
080C- A9 0F LDA #$0F
080E- 85 24 STA $24
0810- 20 BE 08 JSR $08BE
This simply clears the screen ($FC58),
moves the text cursor to VTAB 10, and
calls what turns out to be a simple
text display routine at $08BE.
*8BEL
08BE- 68 PLA
08BF- 85 14 STA $14
08C1- 68 PLA
08C2- 85 15 STA $15
08C4- D0 0B BNE $08D1
08C6- A0 00 LDY #$00
08C8- B1 14 LDA ($14),Y
08CA- 49 96 EOR #$96
08CC- F0 0B BEQ $08D9
08CE- 20 ED FD JSR $FDED
08D1- E6 14 INC $14
08D3- D0 F1 BNE $08C6
08D5- E6 15 INC $15
08D7- D0 ED BNE $08C6
08D9- A5 15 LDA $15
08DB- 48 PHA
08DC- A5 14 LDA $14
08DE- 48 PHA
08DF- 60 RTS
This is a common method of passing
strings to an assembly language
subroutine. (Beautiful Boot also
uses this technique.) Instead of
passing the address of the string,
the string is actually stored within
the code, immediately after the JSR
that calls the subroutine. In this
case, the string occupies $0813-$081F.
The subroutine pulls the address off
the stack (PLA) and stores it in a
zero page address ($14-$15), then
indexes off that address to print the
characters one at a time. ($FDED is
the character printing routine.) Also
interesting is that the string is
stored "encrypted" and each character
is EOR'd with #$96 before being
printed. After each character is
printed, the zero page address itself
is incremented (not the Y register,
which stays at 0 throughout the loop).
This means that when the printing is
done, the subroutine can simply push
the address at $14-$15 back onto the
stack and RTS, and code execution will
resume at the byte after the string
($0820).
*820L
0820- A9 10 LDA #$10
0822- 85 24 STA $24
0824- 20 BE 08 JSR $08BE
This calls the text routine again with
another inline string. Code execution
resumes at $0830.
*830L
0830- A9 00 LDA #$00
0832- A0 00 LDY #$00
0834- 99 00 02 STA $0200,Y
0837- 99 00 03 STA $0300,Y
083A- C8 INY
083B- D0 F7 BNE $0834
This clears pages 2 and 3 of memory.
*83DL
083D- A9 01 LDA #$01
083F- 8D F4 B7 STA $B7F4
0842- AD D1 B5 LDA $B5D1
0845- 8D EC B7 STA $B7EC
0848- AD D2 B5 LDA $B5D2
084B- 8D ED B7 STA $B7ED
084E- A9 00 LDA #$00
0850- 8D F0 B7 STA $B7F0
0853- 8D EB B7 STA $B7EB
0856- A9 02 LDA #$02
0858- 8D F1 B7 STA $B7F1
085B- 20 B7 08 JSR $08B7
According to our well-worn copy of
"Beneath Apple DOS," $B7F4 is part of
the RWTS parmlist.
B7E8 Table type. Must be $01.
B7E9 Slot number times 16.
B7EA Drive number ($01 or $02).
B7EB Volume number expected (0 matches
any volume).
B7EC Track number ($00 to $22).
B7ED Sector number ($00 to $0F).
B7EE Pointer to Device Characteristics
Table (2 bytes).
B7F0 Pointer to user data buffer for
READ/WRITE (2 bytes).
B7F2 Unused.
B7F3 Byte count for partial sector (use
$00 for 256).
B7F4 Command code: 0=SEEK, 1=READ,
2=WRITE, 4=FORMAT.
So we're reading from the track stored
in $B5D1 and the sector stored in $B5D2
and storing it at $0200. What are $B5D1
and $B5D2? Again, "Beneath Apple DOS"
to the rescue. These are part of the
file manager work area and point to the
track and sector of the current file's
track/sector list. In other words, this
is the pointer to the sector that tells
DOS where on disk the rest of this file
is stored. Finally we JSR to $08B7.
*8B7L
08B7- A9 B7 LDA #$B7
08B9- A0 E8 LDY #$E8
08BB- 4C B5 B7 JMP $B7B5
This just calls the normal RWTS entry
point ($B7B5) with the address of the
normal RWTS parameter list ($B7E8). So
it will actually read the track/sector
we set up starting at $083D.
Popping the stack returns us to $085E,
where our adventure continues.
*85EL
085E- A0 01 LDY #$01
0860- B9 00 02 LDA $0200,Y
0863- F0 11 BEQ $0876
0865- 8D EC B7 STA $B7EC
0868- B9 01 02 LDA $0201,Y
086B- 8D ED B7 STA $B7ED
086E- A9 03 LDA #$03
0870- 8D F1 B7 STA $B7F1
0873- 20 B7 08 JSR $08B7
This reads the first sector listed in
the track/sector list at $0200 and
stores it at $0300. The contents of
this sector are actually the second
half of the track/sector list for the
rest of the file. Each sector is 256
bytes and contains track/sector pairs.
Each track and sector is stored as a
single byte, so each sector in the
track/sector list can only hold
information on 128 sectors. But this
file is more than 128 sectors long, so
the track/sector list spills over into
another sector. We now have the entire
track/sector list in $0200-$03FF.
*876L
0876- A9 00 LDA #$00
0878- 8D F0 B7 STA $B7F0
087B- A9 09 LDA #$09
087D- 8D F1 B7 STA $B7F1
Set the starting address ($0900) for
loading the rest of the game into
memory.
*880L
0880- A9 0E LDA #$0E
0882- 8D E0 08 STA $08E0
0885- AC E0 08 LDY $08E0
0888- F0 1C BEQ $08A6
088A- B9 00 02 LDA $0200,Y
088D- F0 25 BEQ $08B4
088F- 8D EC B7 STA $B7EC
0892- C8 INY
0893- B9 00 02 LDA $0200,Y
0896- 8D ED B7 STA $B7ED
0899- C8 INY
089A- 8C E0 08 STY $08E0
089D- 20 B7 08 JSR $08B7
08A0- EE F1 B7 INC $B7F1
08A3- 4C 85 08 JMP $0885
08A6- EE 8C 08 INC $088C
08A9- EE 95 08 INC $0895
08AC- A0 0C LDY #$0C
08AE- 8C E0 08 STY $08E0
08B1- 4C 8A 08 JMP $088A
Load each sector from the track/sector
list into consecutive pages in memory.
Increment the index into the track/
sector list ($08E0) and increment the
target page in memory ($B7F1). When we
run out of sectors in the first half of
the track/sector list ($0200-$02FF),
branch from $0888 to $08A6 and switch
to the second half of the track/sector
list ($0300-$03FF) by incrementing
$088C and $0895. The track/sector list
ends when the track number is #$00
(remember we zeroed out pages 2 and 3),
at which point we branch from $088D to
$08B4.
*8B4L
08B4- 6C FE 08 JMP ($08FE)
Aha! This is the entry point into the
game. By this point, the entire file
has been loaded into memory, and we
perform an indirect jump to the address
listed at $08FE.
*8FE.8FF
08FE- 00 29
So the game's entry point is at $2900.
To trace into the game, we just need to
change this address to point to the
monitor entry ($FF59). Then we can
launch the Quickloader, let it load the
game for us, and it will helpfully dump
us into the monitor when it's done.
*8FE:59 FF
*804G
Now that we have the game in memory,
let's have a look.
Patching MASQUERADE
-------------------
*2900L
2900- EA NOP
2901- EA NOP
2902- EA NOP
2903- A0 00 LDY #$00
2905- A2 16 LDX #$16
2907- B9 00 2A LDA $2A00,Y
290A- 99 00 AA STA $AA00,Y
290D- 88 DEY
290E- D0 F7 BNE $2907
2910- EE 09 29 INC $2909
2913- EE 0C 29 INC $290C
2916- CA DEX
2917- D0 EE BNE $2907
2919- EA NOP
291A- EA NOP
291B- EA NOP
Not sure what those NOPs are for
(possibly commenting out the original
copy protection), but the rest of this
loop does something quite drastic: it
replaces the entire DOS in memory with
a custom one stored at $2A00-$3FFF. If
we had to guess, Side A of the original
game probably required a different DOS
than Side B. Either one was copy-
protected and the other wasn't, or they
were both protected differently. At any
rate, this presents a problem for our
ultimate goal of launching this game
under ProDOS. DOS 3.3 Launcher patches
the DOS area of memory when it "boots"
a virtual disk, which works as long as
- the DOS is fairly standard (this one
wasn't, but we already reauthored the
disk to make it so)
- the DOS isn't hacked, patched, or
replaced once the game is loaded
Now that both sides of the disk are
unprotected (thanks to the original
cracker), we might not even need to
load in an alternate DOS to read Side
B. To test this theory, we can simply
skip the entire loop and attempt to run
the game from the next real instruction
at $291C.
*291CG
To our delight, the game runs just
fine. The original crackers have long
since aligned the DOS on Sides A and B,
and this memory relocation is just a
vestige of a bygone era. But we still
have the original problem: the game
expects us to "flip to Side B," but we
can't do that when we're booting a
virtual disk with DOS 3.3 Launcher.
Two Drives Are Better Than One
------------------------------
Retracing our steps:
]PR#6
<ctrl-C>
]BLOAD MASQUERADE
]CALL -151
*8FE:59 FF
*804G
Now, how do we tell an assembly
language program to switch to drive 2?
If we were at a BASIC prompt, we could
do something like
]BLOAD SOME.FILE,D2
and DOS would load the file from drive
2 and switch the default drive to 2 on
all future commands. But that won't
work for MASQUERADE, because Side B has
no disk catalog and no files. (We
verified this in Copy ][+.) All access
is done with low-level sector reads.
Well, even sector reads are governed by
a parameter list. As we saw when
tracing the Quickloader, the RWTS looks
at a data table starting at $B7E8 for
information about slot, drive, track,
sector, and volume number. $B7EA is the
address of the drive number. Let's
change it and see what happens.
*B7EA:02
Now we insert Side B into drive 2 and
run the game.
*291CG
Disappointingly, this does not read
from Side B in drive 2. The drive 2
indicator light never comes on at all.
The game still tries to read from
drive 1 and gives an error "not a
Masquerade picture disk!"
What to do next? There are only a few
ways to read a sector with a standard
RWTS. "Beneath Apple DOS" teaches us
that the main entry point into the RWTS
is $B7B5. We saw this entry point used
by the Quickloader.
Using the Copy ][+ sector editor,
insert Side A and press "F" to "follow"
a file, and choose the MASQUERADE file.
Scan for hex values "20 B5 B7" (which
translates to JSR $B7B5). Nope, "bytes
not found." Follow the file again (to
start over at its first sector) and
scan for hex values "4C B5 B7" (which
translates to JMP $B7B5). One match,
but it's part of the Quickloader we
examined earlier. No other matches
found in the entire file. Either
MASQUERADE is using an indirect JMP,
or it's using another entry point into
the RWTS.
Digging further into "Beneath Apple
DOS," we find reference to a slighter
lower-level entry point into the RWTS:
$BD00. In fact, $B7B5 does little else
but disable interrupts, JSR $BD00,
re-enable interrupts, and RTS.
Following the MASQUERADE file again, we
scan for "20 00 BD" (JSR $BD00). Aha!
We have a hit. Pressing "L" to view the
disassembly shows us the code around
this JSR. (Keep in mind that Copy ][+
always displays the disassembly as if
it were starting at $0900. In reality,
we may need to do some detective work
to determine where this sector ends up
when MASQUERADE is loaded in memory.)
09DB- A5 77 LDA $77
09DD- 8D F8 49 STA $49F8
09E0- A9 49 LDA #$49
09E2- A0 EF LDY #$EF
09E4- 20 00 BD JSR $BD00
09E7- 90 03 BCC $09EC
09E9- A9 01 LDA #$01
09EB- 60 RTS
09EC- A9 00 LDA #$00
09EE- 60 RTS
After byte $EE, things quickly descend
into madness. 01 60 01 00... That
doesn't appear to be code at all; it's
full of unknown opcodes, BRKs, and
irrational addresses. But look at the
code that, in this disassembly, starts
at $09E0. We're loading the A register
with #$49 and the Y register with #$EF,
then calling the RWTS routine. "Beneath
Apple DOS" states that, at $BD00, "upon
entry, store Y-reg and A-reg at $48,$49
as pointers to the IOB."
Now we understand why setting $B7EA to
#$02 had no effect. MASQUERADE is using
the standard RWTS, but it isn't using
the standard RWTS parmlist at $B7E8.
Instead, it's passing in the address of
its own parmlist, starting at $49EF.
Now the bytes immediately after this
disassembly make sense: 01 60 01 00.
The first byte is the table type
(always #$01). The second byte is the
slot number times 16 (#$60). The third
byte is... the drive number!
What if we patched the drive number in
this parmlist? Would MASQUERADE read
Side B from drive 2? Let's put Side A
in drive 1 and Side B in drive 2, and
find out.
]PR#6
<ctrl-C>
]BLOAD MASQUERADE
]CALL -151
*8FE:59 FF
*804G
Now patch the custom RWTS parmlist...
*49F1:02
...and run the game...
*291CG
Success! When MASQUERADE tells us to
"flip to Side B and hit RETURN,"
hitting RETURN lights up the drive 2
indicator light, loads the opening
picture, and starts the game. Moving
around to a few different rooms within
the game confirms that pictures
continue to be loaded, so it doesn't
appear that the drive number byte in
the custom RWTS parmlist is ever
overwritten or reset back to #$01.
We did it!
Making It Stick
---------------
Now to make our changes permanent. This
is a Quickloader file, so we can't
simply BSAVE it, because that would
ruin it. Instead, we need to go back to
the Copy ][+ sector editor and make our
changes there.
Boot up Copy ][+, open the sector
editor, insert Side A, and "follow" the
MASQUERADE file.
The first change is the game entry
point that the Quickloader will jump to
after loading the rest of the
MASQUERADE file into memory. This is in
the first sector of the file, in bytes
$FE-$FF. (The Quickloader is loaded at
$804, but DOS 3.3 binary files have 4
bytes of metadata before the start of
the code. Thus, the byte numbers in the
sector match up with the lower half of
the memory addresses when the
Quickloader is in memory.) Bytes
$FE-$FF currently hold "00 29". We need
to change that to "1C 29" (to skip over
the DOS-destroying memory relocation
loop we found at $2900) and write the
sector to disk.
The second change is in the custom RWTS
parmlist. This was located in the same
sector as the JSR $BD00, so let's
rescan for hex values "20 00 BD" to
find that sector again. The offending
byte is located at $F1; we need to
change it from "01" to "02" and write
the sector back to disk.
Rebooting this disk with Side B in
drive 2 confirms that the changes were
successful: MASQUERADE loads itself
completely from Side A in drive 1, then
switches to Side B in drive 2.
Virtual Is The New Real
-----------------------
Making a combined two-disk disk image
in DOS 3.3 Copier is easy, but here's
a quick recipe in case you've never
done it. (We certainly hadn't!)
- Select "Copy Disks"
- Press the right arrow to change
"Number of 5.25 Inch Disks" from
1 to 2
- Leave the Slot and Drive alone
- Enter the target directory
- Enter the target filename (we used
"MASQ.2DRIVE")
- Leave "Automatically slow down to
1Mhz" alone
Press RETURN and it will ask you to
insert disk 1. Insert the reauthored
and patched Side A into drive 1 and
press a key. It will spin for a while,
then it will ask you to insert disk 2.
Insert Side B into drive 1 (not 2) and
press a key. When it finishes, you will
have a single file in your target
directory with ProDOS file type $F1
that is 564 blocks long. This is the
combined two-disks-in-one disk image,
which you can launch with DOS 3.3
Launcher.
Enjoy this classic adventure directly
from your hard drive, courtesy of
The C700 Club!
[Download MASQUERADE]