Research / Megadrive Programming / Editing the game code

In this chapter we'll learn how to search for wanted code and edit it.
But before that, I'll show some arithmetic and data functions.



First, the LEA (Load Effective Address) instruction. It loads an effective address into an address register. You should use this instead of MOVEA. Examples:
	lea	($FFFFFE10).w,a1

lea ($12345).l,a2
lea (Label).l,a3
lea 4(a0),a4
lea 4(pc),a5


lea 4(a0),a4 loads the value at a0 + 4 into a4. lea 4(pc),a5 loads PC + 4 into a5.




You can multiply and divide in 68k (which I didn't know for years, really XD). There are 4 instructions for it: DIVS (DIVide Signed), DIVU (DIVide Unsigned), MULS (MULtiply Signed), MULU (MULtiply unsigned). Examples:



	divu	#5,d0	;divide d0 value by 5 (unsigned)

divs d1,d2 ;divide d2 value by d1 value (signed)
mulu #3,d3 ;multiply d3 value by 3 (unsigned)
muls d3,d0 ;multiply d0 value by d3 value (signed)




As you know or not, unsigned $FE is $FE and signed $FE is -$2. There's a difference between multiplying by $FE and -$2:

	moveq	#4,d0

move.l d0,d1
mulu #-$2,d0 ; d0 = $4 * $FE, d0 = $3F8
muls #-$2,d1 ; d1 = $4 * -$2, d1 = -$8 ($F8)




As you can see there is a difference. The last three instructions are different from the ones you have seen already. NOP does nothing. It really doesn't XD. It is used to make VERY small delays, to let hardware parts to act. Example (it doesn't do anything):


	move.w	#4,d0

nop ;it really does nothing =P
moveq #0,d0


The second instruction is ILLEGAL.
It's not a real instruction, it's just a random word (depending on the
assembler, normally TAS.B something) that will cause an illegal
instruction interrupt (interrupts will be covered later).



And the last one, DC. This instruction is to store data in ROM. Can be used as DC.B, DC.W and DC.L. I'll better show an example:


	dc.b	$12,$24,$31,$21,$21,$31,$31,$12

;this will store $1224312121313112 in ROM.

dc.w $0EEE, $0CCC, $0AAA, $0888, $0666, $0444, $0222, $0000
;a random palette ;)

dc.l $1,$123141,$14144144

dc.w %1110,1000


The possibilities are endless. Here's our first real example:



	lea	(Palette_Sega).l,a0

lea ($FFFFFA80).w,a1
move.w (a0)+,(a1)+
rts

Palette_Sega: dc.w $EEE, $EEA, $EE4, $EC0
dc.w $EE4, $EEA, $EEC, $EEA
dc.w $EEA, $EEA, $EEA, $EEA
dc.w $EEC, $EEA, $EE4, $EC0
dc.w $EC0, $EC0, $EEC, $EEA
dc.w $EE4, $EC0, $EA0, $E60
dc.w $EEA, $EE4, $EC0, $EA0
dc.w $E80, $E00


It loads the palette address to a0 and the palette address in ram to a1. Copies first color/colour (a0) to the first color in ram, then increments a0 and a1. This really works =)



Now what I promised - editing the code in the game. Open the Sonic 1 disassembly. Search it for label 'PlayLevel:'. There's a bunch of variable settings (a list of RAM variables is in Guides>Sonic 1>RAM).
I commented some stuff there. The thing we are interested in is editing
the lives count. The RAM location that stores lifes is $FFFFFE12. Change it to 4.
Now, how do we assemble our edited code? I made a batch program which
assembles a code using SNASM68K. Call it 'asm.bat' or something and put
this in there:


@echo off

echo asm.bat by drx

if "%1"=="" goto noinput

snasm68k.exe -emax 0 -p -o ae- %1.asm, %1.bin
goto end

:noinput

set /p input= Input asm file
set /p output= Output bin file

snasm68k.exe -emax 0 -p -o ae- %input%, %output%

:end


There are two options of using it:


1) Open it normally, type the input asm file and the output bin file
and it will assemble it
2) Go to the command line, type 'asm FILE' (without the quotes) and
it'll assemble FILE.asm to FILE.bin (it'll show the errors and the
window won't disappear in Win XP...)



Now open the assembled file in an emulator. If you made it correctly, you should have 4 lives.



Few things that are different in disasm from what I said before:



1) Bcc.s or Bcc.w - this is wrong. You shouldn't use this, assembler will choose the correct one. .s is a one byte branch (-127..127) and .w is a two bytes branch (-32767..32767).



2) There are instructions like CMPI, CMPA, ADDA, ADDI etc. That are just different types of the same instruction, you should just use CMP or ADD and the assembler will choose the correct one. The exception is MOVEM, MOVEQ, ADDQ, xQ etc.



If you understand 90% of what has been written here, then it's good.
Hell, if you understand 20% of it, it's good XD. Assembler is not easy,
and a smallest error can ruin all your work.



"50% of the time spent programming an assembly language is correcting bugs." - an assembly programmer XD



The more time you spend on it, the better you are at fixing those bugs XD



The most common bugs:



1) You MUST put a # before an immediate value (move.b #1,d0) If you won't, this will be treated as an address.


2) When you move a longword into d0, then a byte, the upper bytes still are there! To clear d0 use moveq #0,d0 or clr.l d0

3) This is the most annoying bug ever. PC must be ALWAYS

even. if you insert one byte somewhere, the code after it will be all
in odd addresses and the Genesis will crash. Also, try to load even ROM
addresses into address registers, because that can be nasty, too. The
best method to make sure an address will be always even is to put this
before it:


	align $2


or this:


	even

This will make it always even. But remember, don't put it between the
code, or it'll give you an illegal operation (it SHOULDN'T, because the
PC is always even but...)



You can align as many bytes as you want, eg putting this at the end will make the output rom size be a multiplication of $80000:


	align $80000

With the knowledge you have (I hope you do) you should be able to find
and edit the basic things like lives. Remember, the best method to
learn is to discover everything on your own. Don't rely only on my
guides, be creative =)



Next chapter will cover loops and arrays (not C-like arrays...). Have fun.

Back | Printer friendly
<< 4. Logical operations | 6. Loops & arrays >>

© 2004, 2005 drx, www.hacking-cult.org. Don't copy without permission yadda yadda yadda.