Thrust Hacking Tips

By Mark Bellis

Originally published in EUG #28

Thanks for the tip in EUG #26, Rob.

For everyone's benefit, my objective is to save a memory dump of THRUST, in order to extract routines for my own games, etc, especially the hardware scrolling routine Thrust uses - the screen scrolls by a fixed amount when your ship reaches a set distance from the edge of the screen.

I had the usual problems, in that as with most games the author uses EORing routines to make life difficult for hackers. In the case of Thrust, there is more than one EORing routine, as well as the code and data relocation routines. Previous attempts to decypher the code after running one EORing routine have yielded mostly gibberish, so I was looking for another method of getting the decoded game onto a disk.

Following Rob's tip in EUG #26 of causing an IRQ by pulling the NIRQ pin of the 1MHz bus low (by shorting pin 7 to pin 8 with a screwdriver) and trapping the interrupt in a Sideways RAM image with servie call &05, I have managed to save most of the game to disk in decoded form. (The parts which remain are &D00 to &DFF - the NMI workspace - as I decided that saving to tape was too laborious, being used to disk systems with instant file access; and &700 to &780 - the CLI buffer - which is corrupted by *SAVEing the contents of memory to disk). Attempts to *SAVE to memory from the Sideways RAM image failed. I shall save the missing bits later, using a memory relocation routine in the Sideways RAM image.

The machine configuration was as follows for the successful save:

Master 128 with Double DS DD 5.25" drive

Important *CONFIGURE parameters:   Mode 128 - important so as not to wipe main memory when BREAK is pressed
File 9 - ADFS
No Directory
No Boot - autoboot would be a nuisance

The program THHACK was used to create a ROM image in sideways RAM which would intercept the unrecognised interrupt and disable the wipe-on-break function of *FX200,3. This program is on the disk.

The following is a record of what happened:

*FX151,78,127 disable all IRQs
CTRL-BREAK reset machine. Since the IRQs are disabled, the machine is reset to its power-on state. This technique will clear even the mysterious ROM-not-found-even-when-it-wasn't-*UNPLUGged errors (See later).
*DIR $.THRUST Move to ADFS directory containing THHACK and Thrust.
CHAIN"THHACK" Run hacking program.
PRINT O%
e.g.944
Get location of end of source code (O% is used on the Master series to enable code to be set to run elsewhere in memory - in this case at &8000)
*SAVE THHACK? 900 945 8000 Save the RAM image.
*SRLOAD THHACK? 8000 4 Q Load RAM image into SWRAM bank 4.
CTRL-BREAK Reset to ADFS, Mode 128 and install RAM image.
*DIR $.THRUST Go to THRUST directory.
*THRUST Run THRUST game.

Lift computer, ready to interrupt the game.

Short pins 7-8 of 1MHz bus with a screwdriver.

  The machine locks up, as the error stack has filled up, due to "switch bounce" on the 1MHz bus pins, and has begun to overwrite the stack.

N.B. - I noticed that Thrust intercepts the IRQ1V vector - if it had masked all the IRQs it didn't use (by returning with RTI instead of exiting via the old contents of the vector), then it might not have permitted the MOS to pass the 1MHz bus interrupt round the paged ROMs for the THHACK ROM to intercept it.

CTRL-BREAK This is safe, as the ROM code has disabled the wipe-on-break with *FX200,0

The machine is reset to mode 128 and BASIC, but ADFS is nowhere to be found!

CTRL-D-BREAK Try DFS instead.

Grab a DFS disk.

*SAVE MEM 0 8000 Save memory to disk (no point saving memory above &8000 as it will contain the DFS ROM, workspace and the MOS)
*SAVE MEM2 E00 8000 Save a block of memory which can be reloaded (attempting to reload a file over MOS variables or NMI workspace will cause a crash)
*DUMP MEM (CTRL-N) Look through memory dump for messages such as "Game over" or "Press space to start", and note their locations in memory (in some games the messages appear backwards - note the location of the last letter or trailing null byte in this case).

And so the process continues...

Find routines that point to the messages, and also ones which change vectors or poke the CRTC registers (Not sure about Elk equivalent here).

You will need a table of opcodes, such as is provided in part 2 of the MASTER REFERENCE MANUAL, in order to work in hex.

I have found the following program useful for finding routines which use key locations:

      10FORI%=&xxxx TO &yyyy
      20IF ?I%=&ll AND ?(I%+1)=&hh THEN PRINT (I%-1),!(I%-1)
      30NEXTI%
The program searches from &xxxx to &yyyy for all occurrences of instructions which use location &hhll.

The output is as follows:

2416   603456AD         = LDA&5634 : RTS, beginning at &2416
N.B. Beware of bogus output from data areas - check the surrounding code in each case.

To trace a routine which points to a message, set &hhll to be the location of the message which you noted down from using *DUMP.

An example to find pokes or peeks to the CRTC registers:

      20IF ?I%<=1 AND ?(I%+1)=&FE THEN PRINT (I%-1),!(I%-1)
Using the above line as line 20 will yield all the uses of locations &FE00 and &FE01 - I shall use this to find the hardware scrolling routines by looking at what is loaded into X and Y registers before the pokes - if X = &0C or &0D and the program gives:
mmmm   xxFE008E         = STX&FE00
this will indicate that the game is poking the CRTC registers which control hardware scrolling from location mmmm.

As well as looking for this, and routines which perform a loop containing:

LDX #0         initialise index register
.loop
LDA (&zz),X    get character of message
JSR oswrch     print character
CMP #0         test for null character
BEQ exit       exit the loop if the last character has been printed
INX            increment the index register
BNE loop       loop back for another character (the message is unlikely
               to be as long as 256 bytes, so the Z flag will be 
               non-zero so BNE will act as a branch-always)
.exit
...
I shall look for call to system routines: xxFFF420 = JSR osbyte etc...

To find sprites (this is the reason why I saved a reloadable portion of memory earlier), *LOAD MEM2 3000, and you can pick out the sprites on the screen. You can then find the sprite plotting routine by looking for code such as:

LDA#Sprite_Utilitieste_location MOD 256    set two bytes in zero-page to point to
STA&zz                         the sprite location.
LDA#Sprite_Utilitieste_location DIV 256
STA&zz+1
LDY#0
.loop
LDA(&zz),Y     load byte of sprite
STA&ssss,Y     save to screen location
INY
CPY#8          8 = example height of sprite
BCC loop       loop if Y < 8
...

.sprite_location
<data>
I hope that you can decode games as well as this letter - if so, you should each be able to write excellent games for us all to play!

Happy hacking!

Mark Bellis, EUG #28