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 >>