Holycheat!
Holycheat!
I would like to take this very opportunity and send my greetings to:
* Pugsy, for maintaining XML cheat collection archive and Emulator Cheats forum.
* People who contributed to XML cheat collection and Emulator Cheats forum members.
* My teammates in Commodore64 cracking group Nostalgia, of which I’m very proud to be a member.
Erhan Alparslan
syndromtr AT gmail.com
i
Second edition, 17.09.2014.
No part of this publication may be reproduced, distributed, or transmitted in any form or by any means,
including photocopying, recording, or other electronic or mechanical methods, without the prior written
permission of the publisher, except in the case of brief quotations embodied in critical reviews and
certain other noncommercial uses permitted by copyright law.
Cover page’s background image was taken with permission from “The Most Gorgeous Arcade Games wallpaper”,
(C) 2012-2014 by SolidAlexei of deviantART.
MAME (C) Nicola Salmoria and the MAME team. MAME is a registered trademark of Nicola Salmoria. All rights
reserved.
For some arcade games in Section 2 (In-depth cheat finding), info gathered from history.dat for MAME, its
source is http://www.arcade-history.com (C) Alexis Bousiges.
All arcade games are used for informational purposes and are copyrighted by their respective copyright
holders.
ii
TABLE OF CONTENTS
INTRODUCTION ........................................................ v
iii
3. OTHER CHEAT EXAMPLES .......................................... 45
5. LINKS ............................................................ 60
GLOSSARY ............................................................ 61
iv
INTRODUCTION
This guide has been developed as cheat reference source for those of you who want to learn how MAME (Multiple
Arcade Machine Emulator) cheats are found, how they’re installed and what MAME cheat file basics are.
This manual contains all the information you need to examine present cheats, adapting them to other games, using
powerful built-in MAME debugger features efficiently to monitor code, memory region, registers, data and how to
modify code (if needed), in terms of optimization.
In first section, all structures and components of MAME XML cheat file are introduced with all available tags and
examples of their usage. For this purpose, existing cheats from current MAME cheat database are chosen. Finally,
testing an XML cheat file in MAME emulator is briefly explained; in accordance with current MAME cheat file
structure, to assist you in testing your own cheats in MAME emulator.
In second section, from approach to finalized cheats as a result, how several cheats for 9 games were found is
briefly explained with all single steps. Conclusions and final words are emphasized to specialize.
Third section consists of other cheat examples to provide assistance to previous section, as cheats in this section
don’t include all single steps. Only point of view and guidelines are supplied, referring to the cheats in previous
section.
To simplify searching, these tags below can be used in sections two and three:
Section four includes all tips and tricks used for creating this manual. Mastering these skills mentioned in this
section will result in automation of future cheat investigations.
What you can do with this guide is to find valuable machine code and assembler language information for different
kinds of CPUs in several architectures. This will help the reader to understand how instruction code progress is
executed inside CPU and how programming logic works. If you are beginner in programming, 8-bit 6502 instruction
reference will probably be a good start to learn basics of machine language or to improve your skills. That
reference can be found in web links section. From another point of view, more experienced programmers will find
all information needed to master several code flows for various CPU systems.
Also it’s strongly recommended that, for your WIP (work in progress) or finalized MAME cheats; taking notes
including memory locations, conditions found, short disassembly of original code and patched code with comments
in a particular document file in your computer is very essential to save precious time and easy accessing. Keep in
mind that origin of this guide you are reading is a text document consisting of MAME cheats found by the author.
v
MAME Cheat file basics
<cheat desc="Infinite Lives">
<script state="run">
<action>maincpu.pb@E101=06</action>
</script>
</cheat>
Script state can be ON – OFF – CHANGE – RUN and it defines how or when the action will be executed.
In example above, “run” means it’s always enabled, running each frame.
maincpu states the cheat is for main CPU unit. There may be other CPUs as soundcpu, cart or prot.
Example rom: [goldnaxe] Golden Axe, after entering MAME debugger, debugger window title is:
M68000 maincpu Pressing F6 in MAME debugger changes CPU, so
Z80 soundcpu is the second CPU of this game.
Memory types:
Memory sizes:
In this example, memory address is E101. It’s always in hexadecimal format and the number after = will be poked
into this address. Current value of this address (E101) can be checked with MAME debugger by pressing Ctrl + M
when MAME debugger is active and entering E101 in that new program space memory window.
After “=” that’s the memory size (byte in this example) to be poked. And value is 06 (in hexadecimal format).
Result is, byte 0x06 is poked into E101 memory location, type of memory write is program, on main CPU, and this
poke process is executed in every frame. This cheat is taken from game 1942, it states that game code stores
number of lives in memory location E101. Its value is 0x02 when game starts (without cheat of course) and
decreases when a life is lost. When this cheat is enabled, that byte is always 0x06, it’s also displayed at lower left
corner, with ship icons, game code will decrease it to 0x05 when a life is lost, but as the cheat is always executed
(“run”), that byte will be 0x06 again and will be poked into E101.
After this basic cheat above, here come more complex cheats and descriptions:
From same game, 1942; this is a typical example of “Select Starting Level” cheats. Parameter tag defines value of
(param) variable. (param) will be between 1 and 32 (decimal). Script state is “run”, so this cheat is also executed
every frame, will always be active after cheat is turned on. There’s a condition between quotes. That condition is
also checked every time, for main CPU, RAM area, byte value of memory location E103 is checked. If it’s equal to
0x00, the command after > is executed. Like this, POKE main CPU’s RAM area E103 memory location, (param-1).
1
(param) series was mentioned above, as 1 to 32. As “param” can be used with mathematical expressions, here,
(param-1) series becomes 0 to 31. It means, if player selected to start from Level 8, 8 is visible in cheat menu,
param-1 will be 7 and 0x07 will be poked into E103, when its value becomes 0x00. (When a new game starts,
mostly, game code resets memory location of level number, usually 0x00 or 0x01; here, cheat uses this feature and
installs itself when this memory location is set to default.)
Above, there are two cheats taken from [batman]. To use non-linear variables, this special kind of <parameter> tag
can be used. 4 parameters are defined for each cheat. For the first cheat, script state is “run”, so one of these
hexadecimal values (0x02, 0x00, 0x01 and 0x04) is poked into 103BED memory location in every frame. (Cheat’s
name is also “Permanent”.) This memory location is also on main CPU and it’s a RAM cheat.
Second cheat is same but it has “change” state. What does it mean? It’s executed only ONCE when player selects
one of the choices under “Select Temp. Weapon” list and choose one of the 4 options and press ENTER. If “Gas
Grenades” is chosen, byte 0x04 will be poked into memory location 103BED, only once. That’s why cheat
description says “Temporary”.
This is a typical example of Now! cheats. They’re displayed in MAME Cheat menu as “Set” instead of “On” or “Off”.
Script state “on” states that this cheat will be executed only once, when player selects this entry and presses
ENTER over it. Byte 0x01 will be poked into memory location 1092CD only once as above example.
<cheat desc="Invincibility">
<comment>You can still be hurt by other players or by falling down
holes</comment>
<script state="on">
<action>temp0 =maincpu.mb@00ADCA</action>
</script>
<script state="run">
<action>maincpu.mb@00ADCA=60</action>
</script>
<script state="off">
<action>maincpu.mb@00ADCA=temp0 </action>
</script>
</cheat>
2
Here is the first example of a ROM cheat. While dealing with RAM cheats before, only plain values were stored to
appropriate RAM memory locations (values responsible for number of lives, timer value, energy value etc). For
ROM cheats, mostly, operation code (hexadecimal equivalent of instruction code) itself or part of it, or a code
modification (modified branch address), or part of game code to be added are poked into ROM area.
ROM cheat should provide a handy backup-restore function because most games have RAM/ROM checks just
after game boots. There, RAM/ROM regions are checked with hash control routines (as CRC or modern day MD5
algorithm) and result is compared with checksum(s) to avoid code or data modification. If checksums do not
match, RAM/ROM or write error message is displayed on screen and game boot operation halts.
1) Script state “on” runs only once, just after this cheat is turned ON, on switching phase. It gets the byte value of
memory 00ADCA in ROM region and stores the byte into temp0 variable (backup operation).
2) Script state “run” will be executed every frame, and will poke 0x60 to memory location 00ADCA in ROM region.
3) Script state “off” will run only once, but opposite of first step, when this cheat is ON and it’s turned OFF
afterwards, another switching phase, it stores value of temp0 variable into 00ADCA (restore operation).
This is an ideal use of ROM cheat, in 3 steps; backup, every frame poking progress and restore. But what’s the
byte 0x60 refers to? It’s not a plain variable. To understand its function, disassembly of game code around
00ADCA is needed. To get the disassembled code via MAME debugger:
- While MAME debugger is active with Debug: goldnaxe – M68000 “:maincpu” title,
- Pressing Ctrl + D will open new disassembly window. On that window, typing 00ADCA (onto curpc) and pressing
ENTER will display game code starting from 00ADBC. Here is debugger command to get disassembled code into
text file:
- dasm d.txt,adba,20 (disassembly from 00ADBA to 00ADBA+0x20 is dumped into file d.txt)
- Here is disassembly: (00ADBA is selected as start address to check previous code and after ;
Instruction code comments are added)
00ADBA: 206E 0028 movea.l ($28,A6), A0 ;read (A6+$28) and store to A0 in 32 bits
00ADBE: 1028 FFFB move.b (-$5,A0), D0 ;read (A0-$05) and store to D0 in byte
00ADC2: 6B00 00E8 bmi $aeac ;if result is negative, branch AEAC
00ADC6: B02E 0023 cmp.b ($23,A6), D0 ;compare byte in (A6+$28) with D0
00ADCA: 6600 00E0 bne $aeac ;if they’re not equal, branch AEAC
00ADCE: 6100 FE80 bsr $ac50 ;branch to subroutine at AC50, and return
00ADD2: 7000 moveq #$0, D0 ;here and go on...
00ADD4: 7600 moveq #$0, D3
00ADD6: 4BF9 FFFF C000 lea $ffffc000.l, A5
00ADCA: 6600 00E0 bne $aeac ;if value in (A6+$23) != D0, branch AEAC
ROM cheat modified game code, BNE instruction became BRA (Branch Always). So, when cheat is on, branch
happens at 00ADCA and game code starting from 00ADCE and forward is omitted. From coder’s view, it makes
sense that game code after 00ADD2 has energy lose routine. With that BRA operation code, that routine is
bypassed and invincibility is provided. Also, this is the first example of reverse engineering an existing cheat.
Here is an example of double condition checks before installing a cheat, from [gigawing]. If word (2 bytes) value of
FFAE74 memory location in RAM area is greater than 0x0FC0 AND lower than 0x1000 [0FC0 < (FFAE74) < 1000]
0x0000 will be poked into FFAE74. More conditions can be used as GE (greater or equal), LE (lower or equal), !=
(not equal). This command in MAME debugger will provide further info about them: help expression
3
<cheat desc="Infinite Time">
<script state="on">
<action>temp0 =maincpu.pb@E004</action>
</script>
<script state="run">
<action condition="(frame % 60 == 0) AND
(maincpu.pb@E004!=temp0 )">maincpu.pb@E004=05</action>
</script>
</cheat>
From [kungfum], this is the example of frame variable. Game runs at 55 Hz. Firstly, byte value of E004 is
backupped to temp0 variable. “frame” variable starts from 0 and is increased one by one for each frame is
displayed. So, first condition is frame MOD 60 what means, for frames 0, 60, 120, 180 … this value will be 00
(Each 60 frame ~ 1 seconds). Other condition check is, getting new value of E004 and comparing it with temp0
(backup value). Result is, if each 60 frames passed (delay occured) AND current value of E004 is not equal to its
backup value (it proves E004 has changed), 0x05 is stored to E004. This poke progress is repeated after 60 new
frames.
Cheat is from [3wonders]. When this cheat is executed (“on” means one hit cheat), 0x20 (BITWISE OR) (value of
FF0EC0 BINARY AND 0xDF) is poked back to FF0EC0. Description below:
- | sign is for BITWISE OR (for more, in MAME debugger, help expression command).
- BAND is for bitwise AND. As all numbers are in hexadecimal format here, performed operation is in BINARY
origin.
- ~ means binary opposite, NOT, ~20 is 0xDF in hexadecimal or 11011111 in decimal.
- Also, BASIC equivalent of the cheat above is:
POKE $FF0EC0, ($20 OR (PEEK($FF0EC0) AND %11011111))
- Result is, with bitwise operations, bit 5 of FF0EC0 will be always 1, other all bits will keep their original values.
These bitwise cheats are mainly used for rapid fire, changing some movements those are stored in bits, changing
debug bit values without affecting other bits etc.
Above cheat is from [apparel]. <output> and <argument> tags are used for displaying values from memory
locations with a defined output format. Mostly used in quiz and mahjong games to display solutions or tiles from
these games. %02X means all output format will be in 2 digits hexadecimal, argument count states end value is 14
(decimal) and when this cheat is turned ON, memory values from F406 to F406+0E will be displayed as
hexadecimal numbers in upper left corner, each frame, when values change, their display on screen will also be
updated.
4
Testing a single cheat in MAME emulator:
To test a single cheat (or an XML cheat file) inside MAME emulator, these steps need to be performed:
Here is a test cheat file, finalized with “mamecheat” tags, saved as 1942.xml, copied into cheat_temp folder and
game started with: mame.exe -cheat -cheatpath cheat_temp;cheat 1942
<mamecheat version="1">
<cheat desc="Infinite Lives">
<script state="run">
<action>maincpu.pb@E101=06</action>
</script>
</cheat>
</mamecheat>
5
[footchmp] Football Champ (World) (C) 1991 TAITO
Goal: As explained in this video (http://www.youtube.com/watch?v=wsKTK2LxXh0), this game has a hidden feature
that, when some conditions are met, player can perform a “Super Shoot”.
Known facts: Current MAME cheats have Timer and Score entries, so they can be used as a start.
OK. As a start, I check current cheats like this below (Infinite time cheat):
<action>maincpu.pb@102929=3C</action>
<action>maincpu.pb@10A335=3C</action>
<action>maincpu.pb@10A334=63</action>
After checking values from memory locations 10A334-10A335-102929 with MAME debugger, I notice that, word
value in 10A334 always have the current timer value in hexadecimal format, like examples below:
Another helpful entry is PL1 score. Its value is at 10A332. Also, PL2 score is at 10A333. To find the responsible
routine for Super Shoot, I need to perform same steps what’s described in the youtube link above. First I start a
1UP game, after game starts, I start checking 10A332 with: wp 10a332,1,r command in MAME debugger. While
some playing, that watchpoint is not triggered so far. When I perform these, let’s see what happens:
- My captain player (number 8 – I chose him after game had started) has the ball, he has the ball in certain field as
described, and i give high pass to my teammate with Button B. Last step: I again give a high pass with Button B, to
my captain this time…
- MAME debugger stops with this message: “Stopped at watchpoint 1 reading byte from 10A332 (PC=1F166)”
- So, game code around 1F166 is probably responsible for Super Shoot. Let’s check further with disassembly:
- 10A312, 10A342 and 104636 are unknown. Their values are checked, but we don’t know why.
- PL1’s score must be at least 1 to perform a Super Shoot.
- Also score must be a tie (1-1, 2-2, 3-3 …).
- Timer must be between 10 and 30 seconds (remaining time).
6
To clarify those 3 unknown memory locations, I apply those: wp 10a312,1,w , wp 10a342,1,w , wp 104636,1,w
- 10A312 is FFFF when ball is uncontrolled, 01xx when ball is on other team’s control. When its value is 0X (05 for
example), our player but player number 6 has the control of the ball (+1). This also proves that ball control should
be uncontrolled, high pass from our teammate should start, to our chosen captain player.
- 10A342 is 00.
- 104636 is 01. After further investigate with this in MAME debugger, bp 1f150 (start of Super Shoot checks) when I
try to perform a Super Shoot with another player (e.g. number 11) and debugger stops at 1f150 but A4 register is
different = 104A8E, so 104A8E+$28 is checked as byte, it’s 104AB6 and it’s value is 00. That’s why Super Shoot
routine is terminated. It means, 01F15C: tst.b ($28,A4) is checking captain player. It means its value is 0x01 only
when our chosen captain player is about to take the high pass given with Button B.
To finalize what 10A342 is for, I use a breakpoint trick to poke a specific value into ROM and execute Super Shoot
routine:
It seems 10A342 value is set 0xFF so this Super Shoot is probably happens only once in each game. If you
investigate further for 10A342, you’ll notice that 10A342 won’t be reset to 0x00 until a new coin is inserted after
game over happens. Result is, by default, Super Shoot can be performed only once for each coin.
- 10A342 check should be removed/bypassed, so Super Shoot will be available more than once in each game.
- tst.b ($28,A4) should be preserved. Because, only captain players will be able to perform Super Shoot.
- PL1 – PL2 score checks should be removed / bypassed.
- Timer values should be adjusted. Super Shoot will be available every time, but as Super Shoot sequence takes
around 5 seconds, it’s better to deny Super Shoot if less than 5 seconds are remaining, to avoid graphics init bugs.
Original code:
Here is game code after cheats are applied: (Changes are in red color)
01F156: 4A79 2342 6674 tst.w $23426674.l ;4A79 is null test.w with 6 bytes,
01F15C: 4A2C 0028 tst.b ($28,A4) ;( tst.b ($2342,A5) bne $1f1d0) is now
01F160: 676E beq $1f1d0 ;disabled with a smart instruction code
01F162: 102D 2332 move.b ($2332,A5), D0
01F166: 6006 bra $1f16e ;we bypass PL1 and PL2 score checks and
01F168: B02D 2333 cmp.b ($2333,A5), D0 ;branch further to 1F16E (timer check)
01F16C: 6662 bne $1f1d0
01F16E: 302D 2334 move.w ($2334,A5), D0 ;below, 0C78 null cmpi.w with 6 bytes:
01F172: 0C78 001E 6E58 cmpi.w #$1e, $6e58.w ;(cmpi.w #$1e, D0 bgt $1f1d0) disabled
01F178: 0C40 0005 cmpi.w #$5, D0 ;comparing with 0x05 instead of 0x0A
01F17C: 6D52 blt $1f1d0 ;if less than 5 seconds, no Super Shoot
7
MAME cheat in XML format:
Final words: Now it’s time to test the cheat (very detailed) and make sure that cheat does not destroy other
parameters as captain player’s location on field, to perform Super Shoot. (Confirmed, OK) Next step is, applying
same cheat to clones of this game. ([euroch92] and [hthero]) You’ll notice that [euroch92] code is slightly different,
by default; it allows Super Shoots when remaining time is between 10 and 36 seconds, and if score is 0-0, Super
Shoot is possible.
As code was examined in every way and all requirements of Super Shoot were totally clear then, I contacted
authors of http://www.arcade-history.com (Team supplying history.dat file for MAME) and contributed this info
about requirements of Super Shoot in Football Champ game and for its clones. This info is already included.
8
[timeplt] Time Pilot (C) 1982 KONAMI
“Time Pilot was released in November 1982.
Yoshiki was told to design a driving game. When he learned of the
game's concept, he balked at making it and started on Time Pilot. As
development continued, Okamoto showed his boss design docs for the
driving game, all the while working on Time Pilot. Although his boss told
him to do the driving game instead, he tried to take the credit for Time
Pilot. Okamoto decided not to disgrace his boss and let the episode go!!
The background moves in the opposite direction to the player's plane,
rather than the other way around; the player's plane always remains in
the center.
Stage 1 (A.D. 1910) and Stage 5 (A.D. 2001) are never played in the
attract mode.”
As we start, first thing to investigate is, trying to get memory location where current stage number is stored. We can
assume that, stage number increases linearly, so for our first attempt, we’ll try this method.
MAME debugger has a powerful cheat search engine, so here is some info about it:
- cheatinit or (ci) command initializes a cheat search, if no parameters are used, it will start monitoring whole
changeable memory of main CPU. Default width will be unsigned bytes.
- cheatnext or (cn) command will make comparisons with the last matches. For example, after ci command, cn +,1
command picks memory locations where its value is increased by one and start to monitor them further. It means
all memory locations that were not increased by one are eliminated; they’re out of cheat searching.
- Starting a 1UP game, and when we’re on stage 1, entering MAME debugger and initializing a cheat search by ci
command. It says 2560 cheats initialized.
- We have to progress further in game, to reach incoming stages, so existing cheats can be used like “Invincibility”
and “Put Stage Guardian On Now!”.
- When we reach stage 2, entering MAME debugger again and checking increased value with cn +,1 command. It
says around 25 cheats found. So, as you see, results are narrowed. After some playing, as you’re still at stage 2,
you can also try cn eq command as we’re in same stage, eq means equal (to narrow down results).
- More game play, and as we reach stage 3, cn +,1 command again, and it says 11 cheats found. We have two
options here, as results are very low:
1) cheatlist command to see all those 11 memory locations in MAME debugger console. (It’s recommended to note
them down for further analysis – only one of them has the current stage number value and proper for final cheat.)
Also cheatlist <filename> command can be used to save the results in basic XML format to <filename>.
2) Go on playing, reaching next stage and repeating cn +,1 command again. Result is, still 9 cheats, it’s time to list
them and investigate (Some entries start with value 5C so they’re omitted, as we expect stage number starts from
0 or 1):
9
Now, it’s time to change these values from MAME debugger memory windows that we open with Ctrl + M. Here is
the result of changing those bytes (Before, it’s better to save state with Shift + F7 when current stage is 4 – And
apply each step below, after loading state again):
According to tests, AD04 seems only data for stage info for now. It needs to be investigated further, when poke
operation happens, so, here are the steps:
- Reset game with F3 key, after reset, in MAME debugger, wp ad04,1,w to monitor write operations.
- Insert coin(s) and start a new game, it will stop at that watchpoint.
- Same time, Ctrl + M for monitor window and display AD04 memory location and change its value with 04.
- Close debugger and return to game. We tested same thing again, but here is another result, “Stage 1” appeared
as stage number, but enemies from stage 4 started to appear.
Probably, there are two memory locations responsible for current stage. First one might be real number, i.e. if
current stage is 18, number 18 to display it on screen, and other location is AD04, responsible for one of 5 different
stages, it seems its value can be 00 to 04 to determine stage characteristics.
When wp ad04,1,w is active, resetting and starting a new game again, debugger stops at PC=4C8C. Afterwards, a
new disassembly window with Ctrl + D, changing “curpc” entry with “curpc-10”, to start from backwards:
To understand how this routine works, here is last step. Remove that watchpoint and this time create a breakpoint
as bp 4c78, very start of this routine. When it stops there, step into with F11 key; execute instruction codes one by
one. Result is, $AD32 is zero, so HL=$AD10, DE=$AD00, BC=$10, ldir command copies bytes in region $AD10 to
$AD20 into $AD00. So, original value comes from $AD14, for that $AD04. Afterwards, in game, $AD04 is in use.
Also, this is worth a try: As we know, this game supports 2 player mode, so, probably, $AD32 is second player
byte, and according to above routine, $AD20 to $AD30 area might be default 2UP data? Let’s check it. As bp 4c78
is still active, after resetting game and starting a 2UP game this time, result is:
Stopped at 4C78 first time, $AD32 is 00 and it’s Player 1’s turn. After losing a life,
Stopped at 4C78 again, $AD32 is 01. Applying F11 as single step, HL became $AD20 and ($AD20-$AD30) block
copied to AD00. It’s Player 2’s turn now.
So, for now: Stage character value first appearance is at $AD14 for PL1, at $AD24 for PL2. (It’s copied to $AD04
afterwards.)
Now, last step to find “displayed” stage number, it should be one of the bytes in $AD00-$AD10 region. You
remember previous cheat search?
Time to test this value, this time, at the end of a level, so, start a new 1UP game and again use “Invincibility” and
“Put Stage Guardian On Now!” cheats, when stage 1 boss is killed, manually enter MAME debugger and poke
0x05 into AD01. Yes, “Stage 6” appeared for next stage, as $AD04 is same, enemies are from stage 2 though. So,
here is combined last test for 1UP game:
Start a new game and enter wp ad11,1,w and wp ad14,1,w to monitor writes to these locations. After two
watchpoint triggering, manually poke 0x04 into AD11 and 0x03 into AD14. (Stage 4 and characteristic values.)
10
It successfully started from Stage 4. What is the last stage number? Is it unlimited or ends at 100? Let’s see.
Same watchpoints above, this time let’s poke 0x63 into AD11 and 0x04 into AD14. (99 in decimal for stage
number, and 0x04 for characteristics, as 0x04 is its maximum value.) Stage 99 started! Time to enable invincibility
cheat and try to proceed to next stage. Also we notice that stage number is displayed with different blocks just
below high score line. “Stage 101” is not displayed below, and that number is not reset to one, also difficulty goes
on increasing as new stage didn’t start in default Stage 1 difficulty. So, we’ll decide how to install the cheat. It
seems in cheat XML, with parameters, limiting maximum starting stage to 100 will be fine.
This cheat can be installed now (all required data collected), but here is an important info for these kinds of cheats:
- For “Start from Level/Stage” cheats, best way is to use a RAM cheat, as below example:
<action condition="(extra conditions) AND (maincpu.pb@[level]==00)">maincpu.pb@[level]=(param-1)</action>
Extra conditions: They will be explained in further cheat examples, as there should be one or more extra
conditions if game has an attract mode (mostly some time after insert coin screen, demo game is displayed for
most games). With these extra checks, we can use those extra conditions to poke the level number only when a
real game (after coin inserted) starts, so the cheat won’t affect demo game or so.
As we know that this game also has a demo mode (demo games from stages 2, 3 and 4). So, applying the cheat
without (extra conditions) will probably cause demo game misbehaviors. To finalize cheat, we need to find a
parameter (memory location) for demo game. Examining $ADXX might help.
Here is method to investigate, wp ad11,1,w to detect stage number writes, for demo game and for normal 1UP
game, same routine is executed below:
This $AD30 might be special, let’s check with step into F11 key:
- When demo game starts executing this code, $AD30 is read and it’s 0x00.
- When normal 1UP or 2UP game starts executing this code, $AD30 is read and it’s 0xFF.
So, we can finalize cheat, we have (extra condition) that we’ll allow the cheat only if $AD30 is 0xFF.
11
Condition checks if $AD30 is 0xFF and $AD11 is 0x00 for a 1UP game. So, when default stage value (01) is poked
into $AD11, cheat also checks $AD30, if conditions are met, first, pokes selected stage number into $AD11.
Second line of the cheat checks same conditions, plus, checks if $AD11 is equal to (param), and then pokes
[(param-1) in MOD 5] into $AD24. (% means MOD arithmetic operation, in XML file.) So, both required memory
locations responsible of stage are poked with correct values from selected parameters, 1 to 100. As we test this
briefly in all modes (demo game – 1UP – 2UP) in different combinations, cheat works as it’s constructed, without
any problems.
In most shooting games, game code checks fire button with special routines that pressing & holding fire button is
detected and when it happens, shooting is disabled. For this purpose; in most cases, shadow byte is used.
Joystick direction and button presses are read and stored in two bytes. Primary byte, it’s bitwise read and code
branches to appropriate movement or button press routines. Second one is the “shadow” byte; after main byte’s
operation is complete, it’s usually compared with this shadow byte. So, shadow byte allows or denies movements
or button code reading. If fire button is held down; in shadow byte, its bit will be 1 and it will deny reading state of
fire button. So, clearing (or setting if it’s inverted) fire button’s bit in shadow byte will provide rapid fire.
Or, some games count number of bullets on screen and there’s maximum limit for it. Last case, first fired bullet
should reach some coordinate that new bullet is allowed, this is another type of these checks. For button value
check, XOR (exclusive or) commands are mostly used. So, first approach to find these routines is, finding memory
location of fire button and check for read/writes and finally finding that XOR or hold button detection routine.
- Start a new game and when you’re not firing, enter MAME debugger and start a trace: trace <filename>
(Make sure that on MAME debugger, maincpu is displayed, so trace will start for maincpu, all instruction codes
executed will be written to specified file.)
- Quit MAME debugger and immediately press Button A (fire) and then immediately enter MAME debugger again
and stop the trace with: trace off
- Check end of this document, Tips and Tricks section, “Trace file, getting unique instructions” and apply it to main
trace file.
- Examine unique instructions (Instruction codes those were executed only once for that trace session – saved to
new file for example), after you see RET (return from subroutine – end of subroutine) or there is large space
between two addresses, add breakpoints for those routine start addresses in MAME debugger like these:
bp 0f1a , bp 2407 , bp 31e8 , bp 33b8 …
- Check when those breakpoints are triggered, and remove unwanted ones by bpclear <number> command.
- As you notice, for above example, last 2 breakpoints are triggered periodically, even we’re not pressing Button A.
So it’s time to clear them. Also first breakpoint is triggered without firing, time to clear it also.
- Now, only bp 2407 is active, if we check it, as soon as we press Button A, it’s triggered. Now it’s time to
investigate memory around 2407:
So when we remove that breakpoint and start examining from $23F4 by bp 23f4 command, it will stop there every
time, what about bp 2407? It seems it’s only executed if branch at $2405 do not happens. Yes, it makes sense; it
stops at $2407 only fire button is pressed, for the first time, if fire button is hold down, no stop at $2407. So, we
need to find a way to always execute $2407. Above routine checks 3 rightmost bits of $A98E with and $03
operation. If result is $01, it means, if value of $A98E is xxxxxx01 in binary format, fire is allowed. As we monitor
$A98E value periodically, here is example:
12
This above is due to rotate left operations, Carry flag is also important for this, for above example, only first two
entries, 1F and 3F, if AND 0x03 is applied, result is 0x01 and fire happens. Afterwards, no fire. Example of a smart
routine to prevent rapid fire, apart from XOR routine.
What if we compare with 0x00 after AND 0x03? We shouldn’t break current case, normal fire should be allowed,
additionally, fire button hold down should be taken as firing. Testing with bp 23f8,1,{maincpu.mb@2401=00;g}
As we suspected, result is AUTO firing instead of rapid fire, fire button is not pressed but auto firing in progress. So,
last step might be needed for that branch: Using JR Z instead of JR NZ, if Zero flag is 1, branch to $2409. So,
resetting game and starting a new game, bp 23f8,1,{maincpu.mb@2401=00;maincpu.mb@2405=28;g}
This time code compares it with 0x00 and uses JR Z (0x28 is its operation code). Result is perfect, rapid fire is
always active, for both players, when fire button is hold down, rapid fire happens, after it’s released and button is in
idle, no auto firing observed. Here is original code VS cheat installed code:
2405: 28 02 jr z,$2409 ;if ($A98E) and 0x03 is 0x00 then branch 2409
Final words: Start from stage cheat works through levels 1 to 100, demo game is not affected. Rapid fire is always
active when Button A is pressed and hold down. Applying same procedure to [timeplta], [timepltc] and [spaceplt];
clones and bootleg of this game is also required to finalize.
13
[missw96] Miss World ’96 Nude (C) 1996 COMAD
“Rip-off of "Gals Panic!", featuring 'adult' photographs instead of the
cartoon imagery.
…
Like many of Comad's games, the music is comprised of short
MSM5205 samples and loops, which are bootlegs ripped from
various well-known songs without the approval from the original
music authors.
Levels contain samples and loops from these music tracks :
'Oxygene Part IV' by Jean-Michel Jarre, 'Tonight Is The Night' by La
Bouche, 'Let's All Chant' by Michael Zager Band (a modern 90's
remake of this song)”
#reverseengineer #adaptexisting
Known facts: [galsnew] has invincibility cheat found and installed, as game engines are similar, code might be
similar, too. Worth a try.
<action>maincpu.mb@00311E=60</action>
<action>maincpu.mb@005BAE=60</action>
<action>maincpu.mb@00601D=02</action>
Now it’s time to start [galsnew], enter MAME debugger and partially disassemble those 3 memory locations:
What to do is easy, after those notes above, we need to start [missw96] and perform some memory searches. If
we’re lucky, we can find same or equivalent routines to apply the cheat.
For first part of the cheat, let’s search significant bytes as above: In MAME debugger,
- find 0,10000,w.082e,0000 (search 0x082E followed by 0x0000 in memory region 0 – 10000)
- Found at 2AF8 – 6C30 - 6C5E. After disassembling those in MAME debugger with Ctrl + D, we notice that routine
at 2AF8 has the same structure; this is probably the routine to be patched.
Second part of the cheat, a new search (6700 0006 is a short branch so it might be used very few):
- find 0,10000,w.6700,0006,6100,0014
- Found at 5900. After examining, it’s also identical routine.
So it’s time to check these 3 again and construct the cheat, of course based on [galsnew]. That cheat should be
checked by playing, of course.
14
[missw96] original code:
<cheat desc="Invincibility">
<script state="on">
<action>temp0=maincpu.mb@2AFE</action>
<action>temp1=maincpu.mb@5900</action>
<action>temp2=maincpu.mb@5D6B</action>
</script>
<script state="run">
<action>maincpu.mb@2AFE=60</action>
<action>maincpu.mb@5900=60</action>
<action>maincpu.mb@5D6B=02</action>
</script>
<script state="off">
<action>maincpu.mb@2AFE=temp0</action>
<action>maincpu.mb@5900=temp1</action>
<action>maincpu.mb@5D6B=temp2</action>
</script>
</cheat>
Final words: Cheat tested, as it’s supposed to be, in every circumstance invincibility is preserved. So, as this game
has 3 clones ([missmw96], [missw96a] and [missw96b]); it will be an easy approach to apply this cheat on them,
with identical routine search method.
15
[umk3] Ultimate Mortal Kombat 3 (C) 1995 MIDWAY
“An update of "Mortal Kombat 3", with new
characters added to the original MK3 cast.
There are lots of other additions and
goodies in the game (see Updates section
(REV. 1.0)).
- TECHNICAL -
Midway Wolf Unit Hardware
Main CPU : TMS34010 (@ 6.25 Mhz)
Sound CPU : ADSP2105 (@ 10 Mhz) Sound
Chips : DMA-driven (@ 10 Mhz)
Players : 2 Control : 8-Way Joystick Buttons
: 6 = > [1] High Punch, [2] Block, [3] High
Kick = > [4] Low Punch, [5] Low Kick, [6]
Run”
#reverseengineer #adaptexisting
Goal: To adapt “Auto Fatalities” (CPU or Player finishes with fatality or Stage fatality) from [mk2].
Known facts: [mk2] has these 3 cheats; with some luck, they can be transferred to [umk3] successfully.
First, some info about TMS34010 processor, most MIDWAY games used this processor as main CPU. Its ROM
addressing is quite weird, RAM addressing is also different. Examples:
You won’t notice a “3FD8” address in ROM region of TMS34010, so, here is the formula to convert it to TMS34010
address:
TMS34010 CPU addresses individual bits instead of bytes, that’s why it’s multiplied by 8. Also 0x01000000 to
0x013FFFFF is RAM, 0xFF800000 to 0xFFFFFFFF is ROM area; mentioned in MAME sources.
FF81FE40: 09E8 3BA0 FF82 MOVI FF823BA0h,A8 ;store FF823BA0 value to A8 register
FF81FE70: 09C0 0014 MOVI 14h,A0 ;store 0x14 to A0 register
FF81FE90: 0D5F EC20 FF80 CALLA FF80EC20h ;call routine at FF80EC20 and return
FF81FEC0: C904 JRNC FF81FF10h ;if Carry flag is 0, branch FF81FF10
As you can easily guess, reverse calculation is needed to get XML ROM address, like this:
This is real address in RAM area, but that’s how you’ll see in MAME debugger (Ctrl + M memory window):
16
As RAM – ROM handling of TMS34010 is clear now, here are existing cheats for [mk2]:
<action>maincpu.mw@00428D2=temp0 </action>
<action>maincpu.mb@003677F=temp1 </action>
<action condition="param==01 OR param==03">maincpu.mw@00428D2=1002</action>
<action condition="param==01 OR param==02">maincpu.mb@003677F=C0</action>
First entry, 428D2, is converted into real address: 428D2 * 8 + FF800000 = FFA14690
Second entry, 3677F, is converted: 3677F * 8 + FF800000 = FF9B3BF8
As we investigate, especially MOVE *A13(1002h),A4,0 instruction code, A4 should be zero, its pointer is changed
to $1002 to read zero from further memory. So, plan for [umk3] cheat is:
- Find first entry, same or similar routine, and force A4 register to get value 00. (We’ll improve the cheat – first entry
of [mk2] cheat is not reliable, the one we’ll use in [umk3] will be stable.)
- Find second entry, same or similar routine, and remove conditional branch, apply direct branch.
When we run [umk3] and perform these searches from MAME debugger:
- find ff800000,800000,w.5601,4820 ;(search 0x5601 followed by 0x4820 in memory region FF800000 - end)
- Found at FFB5D3B0. Routine is examined, equivalent of above.
- find ff800000,800000,w.1942,0d5f
- Found at FFB29D50. Again, routine is equivalent.
OK. It seems we found equivalents of two different routines responsible for auto fatality, from [mk2] and adapted
them for [umk3]. But when it comes to testing, if you construct a XML cheat from these and start testing, this won’t
work 100% as it should, because other game’s behaviors will differ: [umk3] has “animalities” feature. As a result,
when you test this cheat in this state, you’ll notice:
1) Stage fatalities will have some bugs. (There won’t be auto Stage fatalities 100%.)
2) Auto fatality – CPU fatality will sometimes result in animality.
17
So, from now on, we need to investigate those two routines very detailed to fix those issues. It seems first routine
(we patch with A4=0x02) is responsible for automatic Player / CPU movement on screen before fatality starts, so,
second routine is probably the one responsible of black screen and start of fatality.
What to do:
- Save some states when the cheat is on, and Player / CPU is about to die, on last round with Shift + F7.
- After you save several states; you’ll be able to catch the bugs, will be able to save states of:
Stage fatality enabled, but ended with no Stage fatality (cheat failed).
Auto or CPU fatality enabled, but ended with animality (side effect).
Now it’s time to trace the code and find the branch changes. For normal and buggy states, load them with F7 in
MAME and for start; give bp ffb29d30 command for a breakpoint just before fatality starts. When that breakpoint is
triggered, trace <filename> command and then after operation is over, trace off command. You need to take 4
trace logs, two for 100% success fatalities (normal and stage) and two for unwanted results (stage fatality failed
and animality occured).
If you compare those traces, two by two, here is the result for stage fatality fail:
(Of course, you can compare them manually, or with file compare tools – Where to focus is first changes from very
first start.)
So, we had 2 lines of cheat before, converted from [mk2] and finally added 2 more lines to avoid the bugs, so here
are all of them listed (original code):
To test them, we’re converting these real addresses to XML cheat format with the formula:
[(TMS34010 real address) – FF800000 ] / 8 = (XML ROM address)
18
<cheat desc="Select Perm Fatality Type">
<comment>Automatic Stage Fatality (Use in Kombat Zones - The Subway, Bell
Tower, The Pit III, Scorpion's Lair)</comment>
<parameter>
<item value="0x02">CPU always finishes with a Fatality</item>
<item value="0x01">Automatic Fatality</item>
<item value="0x03">Automatic Stage Fatality</item>
</parameter>
<script state="on">
<action>temp0 =maincpu.mw@006BA80</action>
<action>temp1 =maincpu.mb@00653B3</action>
<action>temp2 =maincpu.mw@00653C0</action>
<action>temp3 =maincpu.mw@0065524</action>
</script>
<script state="run">
<action>maincpu.mw@006BA80=temp0 </action>
<action>maincpu.mb@00653B3=temp1 </action>
<action>maincpu.mw@00653C0=temp2 </action>
<action>maincpu.mw@0065524=temp3 </action>
<action condition="param==01 OR param==03">
maincpu.mw@006BA80=09E4</action>
<action condition="param==01 OR param==02">
maincpu.mb@00653B3=C0</action>
<action condition="param==01 OR param==03">
maincpu.mw@00653C0=0300</action>
<action condition="param==01 OR param==02">
maincpu.mw@0065524=0300</action>
</script>
<script state="off">
<action>maincpu.mw@006BA80=temp0 </action>
<action>maincpu.mb@00653B3=temp1 </action>
<action>maincpu.mw@00653C0=temp2 </action>
<action>maincpu.mw@0065524=temp3 </action>
</script>
</cheat>
Final words: As we test it very detailed, in every ways and combinations, we notice that it’s working like a charm.
But as you might guess, there are still more tasks to do. Firstly, clones of [umk3]. But attentive research needed,
because this one and similar games have different clone game structure! If you check history.dat:
In Updates section, what’s different in REV1.0 – REV1.1 – REV1.2 is mentioned. It seems in all versions; this cheat
is supposed to work, so now it’s time to prepare cheats for the clones ([umk3r10] and [umk3r11]).
Another game in same series, [mk3]: Now for the last step, we need to adapt the cheat above to [mk3]. Again,
version (revision) check needed, so we check history.dat:
- For [mk3] and [mk3r20] we adapt above and apply the cheat with 4 lines:
(movement – fatality start – stage fix – animality fix)
- For [mk3r10] and [mk3p40] we adapt above and apply the cheat with 3 lines (Animality not implemented):
(movement – fatality start – stage fix)
19
[ddpdfk] DoDonPachi Dai-Fukkatsu (C) 1998 CAVE
“- TECHNICAL -
Cave CV1000D Hardware
CPU : Hitachi SH-3 CPU, 133 Mhz Clock Sound Chip : Yamaha
YMZ770C-F Chip U1 : IS42S32400D 128Mb SDRAM @ 166mhz
(standard SH3 has MT48LC2M32 64Mb SDRAM) Chip U4 : 32Mbit
Spansion S29JL032H (standard SH3 has a 16Mbit Spansion
S29AL016D.
- TRIVIA -
DoDonPachi Dai-Fukkatsu was showed fully playable at AOU 2008,
which took place 15-16th February at the Makuhari Messe, Japan.
The game was then released on May 22, 2008 in Japan.
- SERIES -
1. DonPachi (1995) 2. DoDonPachi (1997) 3. Bee Storm -
DoDonPachi II (2001) 4. DoDonPachi Dai-Ou-Jou (2002) 5.
DoDonPachi Dai-Fukkatsu (2008) 6. DoDonPachi Saidaioujou (2012)
7. DoDonPachi Maximum (2012) [Apple App Store]”
Goal: As this game became fully emulated with MAME 0.153, no cheats are present (yet) and at least, preliminary
cheats need to be found and installed.
Known facts: Game is supposed to have same gameplay features like DoDonPachi, also after searching for this
game on internet (both arcade and console port versions), these can be gathered:
- There’s a hidden style, where “Style Select” is displayed, only “Bomb” and “Power” styles are selectable, but
hidden style is named “Strong”.
- There’s a hyper gauge, horizontal gauge just above planes remaining, at upper left corner. When it’s full, hyper
shoot becomes active and it’s the most powerful shooting in game.
- It seems, as DoDonPachi game, this game also has Second Loop feature. First loop has 5 Areas (levels), after
completing them with meeting certain requirements; Second Loop starts from Area 1. But after Second Loop is
completed, special Area 6 is reached (special final boss).
For infinite lives research, stock ci, cn -,1 method in MAME debugger will be fine. But, ci command causes MAME
debugger halt, so we’ll start with ci uw (word search – instead of byte). After starting a 1UP game and ci uw
command at first, and then cn -,1 when a life is lost, finally, we find the memory location, it’s C5C3703. It’s for PL1.
Here is a hint to find the value for PL2 (or all other players available): wp c5c3703,1,w command in MAME
debugger, get PC address (PC=C1F350E), now disable that watchpoint, create a breakpoint for that PC address as
bp c1f350e and wait for that breakpoint is triggered, but for PL2 this time (when PL2 loses a life). If you check
previous instruction code, from MAME debugger window, it’s C1F350C: MOV.W R2,@R4 (move word value of R2
into R4). R2 is 0x0002 then, R4 is C5C3A0E. When this move happens, C5C3A0F becomes 02, number of lives for
PL2 (in byte format). So here is the cheat (0x07 is poked because 6 ships will cover that whole area):
20
Now similar procedure for infinite bombs, this time I can search with bytes like this, as I can assume that PL1
parameters (lives, bombs, energy …) are not in further locations in whole memory. So,
ci ub,c5c0000,10000 (search for bytes, between c5c0000 and c5d0000) and cn -,1 after a bomb is used:
C5C3706 is the memory location for number of bombs for PL1. But when you play game for some time, you’ll
notice that number of bombs can be max. 6 and current bomb display slot allows max. 5. So, next byte after
C5C3706 can be that slot? Probably, because C5C3707 is 05. So we need to poke 0x0606 into C5C3706 for the
bombs of PL1. Above method, getting breakpoint, applying it for PL2 and using a bomb, gives PL2 bomb location,
it’s C5C3A12.
How to find & unlock Strong Style? Here is a tip again, after resetting game and reaching “Style Select” screen,
only “Bomb” and “Power” is visible & selectable. So, there should be one or more memory locations that control
what is selected there, e.g. 0x01 for Bomb, 0x02 for Power, maybe 0x03 for hidden Strong style? So, what to do is,
starting a new cheat search with ci uw when default “Bomb” is selected, and move cursor to “Power” and in
debugger cn +,1 (index increased) and move cursor again to “Bomb” and in debugger cn -,1 (index decreased).
After a few tries, it’s displayed that 6 cheats found. I’ll give it a shot for C88D01E; it was the first entry when
cheatlist command was given. wp c88d01e,1,w command to monitor write operations, we notice that each time
moving cursor, it’s triggered. So, it’s time to be careful and examine the routine that triggers the watchpoint. When
moving cursor from “Bomb” to “Power”, PC is C2A8264. After applying step into (F11) to execute code one by one,
this is interesting:
0C2A826A: 5638 MOV.L @($20,R3),R6 ;move R3+$20 = C88D070 value into R6, it’s 02
0C2A826C: 3263 CMP/GE R6,R2 ;compare if R2 <= R6. R2 is 01, “Power”
0C2A826E: 8FAF BFS $0C2A81D0 ;branch backwards
This routine probably checks navigation between “Bomb” “Power” and the hidden “Strong”, in forward mode,
moving cursor down. So, if manually change 02 value of C88D070 to 03 in MAME debugger memory window,
something very interesting happens: We can navigate down from “Power”, there’s no “Strong” writing there but if
we move the cursor below “Power” and press button, STRONG Style is selected!
So, last steps to finalize this cheat are, setting a special watchpoint for C88D070 (to understand how its initial value
is read by game code).
After player is selected, before Style screen, this watchpoint is triggered. PC=C2A69C6. Here is disassembly:
(On delayed branches, the instruction code after branch is executed, and then branch happens.)
We’re about to find it, so, R0 should be zero at C2A69BC. To investigate it, remove all watchpoints and breakpoints
and then this command: bp c2a69b6 to understand where R0 is set. Now it’s time to wait for breakpoint to be
triggered, and then execute commands one by one with F11 key from MAME debugger. Here it is:
21
0C1EA802: 6020 MOV.B @R2,R0 ;copy byte from R2 (C5C1E1D currently) into R0
0C1EA804: 2008 TST R0,R0 ;it’s zero by default
0C1EA806: 2FE6 MOV.L R14,@-R15
0C1EA808: 0029 MOVT R0 ;move T bit into R0. T bit is 1 so R0 gets 01
0C1EA80A: 2008 TST R0,R0 ;after this test operation, T bit is 0
0C1EA80C: 0029 MOVT R0 ;move T to R0. R0 is again 00
And after performing more “step into” operations with F11 key, we’re sure that R0 value is not changing until
execution of this subroutine is completed. Above is history of R0 register in this subroutine. So, probably C5C1E1D
address responsible of normal or extended modes. When we check memory, its value is 0x00 by default. What if
we change its value to 0x01? Let’s see…
After game reset, manually poking 0x01 into C5C1E1D: When you reach Style select screen, “Strong” style is
displayed, and selectable! Of course, detailed tests should be performed as, starting a game with both players.
(As C5C1E1D cannot reset itself after the cheat is turned on and then off, to reset its value, script state=”off” needs
to be added as below.)
Now it’s time to seek for Hyper Gauge memory location, to find and maximize it for Hyper Shoot. In most games,
value of a gauge (life bar, boss energy indicator, level progress bar etc.) is stored as bytes, words or double words
in specific RAM area. So, what to do is simple: Initializing a new cheat search with ci, when visual display on the
gauge increases, cn + command and repeating this cn + several times until memory location is found.
So here is the result applied to the game: After 1UP game has started, Hyper Gauge is empty. Especially in
modern games, these gauge – progress bar values are stored in words or double words than bytes, we need to
start a new search by ci uw command. And, entering cn + command when we see progress on Hyper Gauge. After
repeating this several times, until Hyper Gauge is full, cheat is found, C5C392E, 0x0000 to 0x0200, as we
guessed, word value. Setting a watchpoint for C5C392E for write operation, getting PC value and setting a
breakpoint for that PC address, and starting a 2UP game will give PL2 Hyper Gauge address, as C5C3C3A. So,
here is the cheat (to avoid permanently Hyper Shooting, current Hyper Gauge value is backupped, it will be
restored when cheat is turned off):
22
<cheat desc="Infinite Hyper Counter PL2">
<comment>Activate with Button D (Control Type A) or Button C (Control Type B).
Provides maximum firepower.</comment>
<script state="on">
<action>temp0=maincpu.pw@0C5C3C3A</action>
</script>
<script state="run">
<action>maincpu.pw@C5C3C3A=0200</action>
</script>
<script state="off">
<action>maincpu.pw@0C5C3C3A=temp0</action>
</script>
</cheat>
Invincibility cheat, it’s a vital cheat and most games support it partially. Especially, after you lose one life in most of
the games, incoming life starts with partial invincibility (i.e. our hero in fighting games or our ship in shooter games
appear blurred – flashing or in a darker color to indicate invulnerability for some seconds). It means there’s mostly
a countdown timer in memory for that invulnerability period. When timer has not reached to zero, player won’t get
hits – wounds. Sometimes additionally there may be a byte, related with that countdown timer address, to store
invulnerability status (i.e. 0x01 for on, 0x00 for off).
So, a simple check and then research is enough, we hope. When we check gameplay, at very first start or after
losing a life, new ship appears with a shield and that shield disappears slowly. Then only thing to do is that shield’s
countdown timer research. As time is limited in this search (shield disappears in a few seconds), we can use a
special cheat init method as below:
In 1UP game, at very first start or after losing a life, immediately, ci uw to initialize cheat search in words, gv
command to wait for new VBLANK happens, cn -,1 to mention countdown timer decreased by one, and then gv
and cn -,1 repeatedly, and cheatlist when number of cheats are low, results are displayed. One cheat memory
location starts from C5C3 (in same memory region as above found cheats). It might be what we’re looking for. Let’s
check that, C5C3886. Yes, after checking its value from monitor window, it starts from 0x0100 and decreases in
time and reset to 0x0000, that time shield is completely gone. There may be a byte (invulnerability) to check if this
value is above zero, to find it, starting a new game again and after our ship is displayed with its shield, wp
c5c3886,1,r command to monitor reading value from this memory location. Read happens from 2 PC addresses,
but one of them, after we trace all routines with F11 key in MAME debugger, unfortunately nothing special happens
when C5C3886 becomes 0x0000, no other bytes control its progress (shield on or off, same time checking
invulnerability), so we need to construct invincibility cheat based on this shield value.
As we test what fixed value to poke into C5C3887, as byte, 0x02 will be fine, because even it’s updated every
frame, if 0x01 is selected, by game code it becomes to 0x00 with decrease and invincibility is removed. So, with
choosing 0x02, game code can only decrease it to 0x01 and invincibility is still on, cheat will update it again to
0x02, also shield is not visible with this value. Also with breakpoint method, we find shield’s memory location for
PL2, it’s C5C3B93 as byte.
Now, it’s time for the Area cheat (ability to select start area). We need to get memory location of current Area at
first, but it will take some time because we need to complete some Areas. As cheat init in byte format is
problematic for this game, let’s initialize a new cheat for Area number. After starting a new game, ci uw to initialize,
above Invincibility cheat may help a lot, after turning it on, we go on playing and when Area 2 appears, cn +,1 and
repeating this in every next Area. Also, saving game state with Shift + F7 will be fine, after defeating Area 1 boss,
because after it, Area 2 will start, so the byte we’ll find later can be monitored with loading that state instead of
playing from the beginning. So, cheatlist reported 6 addresses. 3 of them start with C5C1…. So one of them is
probably holding current Area, because the cheats we found were starting with C5C3….
23
So, it’s time to load that state when Area 1 boss is defeated, and add these watchpoints for write operations:
As we investigate all code around where write operations happened for these 3 above, the result is:
C5C1F33 value is physical Area value, but it’s increased by one after boss is defeated. When you defeat boss in
Area 1, this byte becomes 0x01. When next Area starts (2), its value is read and poked into C5C1F2F. So,
C5C1F2F is responsible for current Area progress, incoming enemies, mid-bosses etc. while playing.
So, first test is, starting directly from Area 2 is possible? Let’s check. After starting a new game, wp c5c1f2f,1,w
command, when it stops, manually changing that byte into 0x01, result is, graphics distorted. We know that normal
game starts with an animation, it seems that animation and Area 1 start is “forced” so it’s not possible to start from
desired Area. Then, what to do? As in DoDonPachi game, we can adapt this cheat as “Select Area to follow Area
1” so, after Area 1 is finished, custom code can force desired Area number and game goes on that way.
Then, we need to investigate C5C1F2F store progress, more detailed this time. Because, there might be another
byte controlling Second Loop. (DoDonPachi game has that feature, normal areas, and Second Loop for the same
areas, and a special last area.) C5C1F33 is set 0x01 at PC=C1F0104, so here is trace log of executed code just
before that address:
So, for the last step, we need to complete all areas (invincibility cheat will help) and start Second Loop and check
memory locations just after C5C1F33. We notice that C5C1F37 and C5C1F3B locations become 0x01 when
Second Loop is active. Here is the table for all available areas:
Very likely, it’s impossible to find free memory in main routines area, to insert our custom code, so hook method
should be tried for this. First, just before this value increment (area value) happens, a proper JSR or JMP needs to
be found, and then, redirecting JMP/JSR branch address to our custom code should be the first step. But most
important part is: Our custom code should not destroy currently used register values! If register values are limited,
backup – restore of registers can be applied.
24
Here is proper way applied as hook & custom code for this game:
...
0C2024E4: D019 MOV.L @($0064,PC),R0 ;PC+$0064 is 0C20254C here, store it into R0
...
0C2024F6: 402B JMP R0 ;jump to R0. (Its value is 0C1F00B0 by default)
Memory location 0C20254C will be the hook point. After examining memory area with Ctrl + M command, we notice
that 0C670000 and afterwards are free, all filled with 0xFF. So, custom code will be installed there.
Above routine checks current Area byte and Second Loop byte at first, current Area byte should be 0x00 (Area 1)
and Second Loop byte must be 0x00 (Second Loop not active) for the cheat to operate (cheat only works after
Area 1 is cleared & no Second Loop), when these conditions are met, according to selected “param”, this code
prepares values to be poked into C5C1F2F – C5C1F37 – C5C1F3B, with conditional checks.
<action>maincpu.pd@0C20254C=0C670000</action>
<action>maincpu.pq@0C670000=6343736050324015</action>
<action>maincpu.pq@0C670008=890F50304015890C</action>
<action>maincpu.pb@0C670010=E0</action>
<action>maincpu.pb@0C670011=param</action>
<action>maincpu.pq@0C670012=E205302689026503</action>
<action>maincpu.pq@0C67001A=A0060009E0011302</action>
<action>maincpu.pq@0C670022=1303E005AFF70009</action>
<action>maincpu.pq@0C67002A=D001402B00090C1F</action>
<action>maincpu.pw@0C670032=00B0</action>
First line shows changed hook address, all other lines have instruction codes of our custom code, but code is split
into two parts because “param” should be stored as a byte. That parameter is selected from cheat menu by player.
On next page, full cheat is displayed with all states and its comment:
25
<cheat desc="Select Area to follow Area 1">
<comment>If you select Second Loop:(harder game) After a cutscene, choose YES.
Area 6 is final stage after Second Loop. </comment>
<parameter>
<item value="0x01">Area 2</item>
<item value="0x02">Area 3</item>
<item value="0x03">Area 4</item>
<item value="0x04">Area 5</item>
<item value="0x05">Second Loop</item>
<item value="0x06">Area 6 (Special)</item>
</parameter>
<script state="on">
<action>temp0 =maincpu.pd@0C20254C</action>
<action>temp1 =maincpu.pq@0C670000</action>
<action>temp2 =maincpu.pq@0C670008</action>
<action>temp3 =maincpu.pq@0C670010</action>
<action>temp4 =maincpu.pq@0C670018</action>
<action>temp5 =maincpu.pq@0C670020</action>
<action>temp6 =maincpu.pq@0C670028</action>
<action>temp7 =maincpu.pd@0C670030</action>
</script>
<script state="run">
<action>maincpu.pd@0C20254C=0C670000</action>
<action>maincpu.pq@0C670000=6343736050324015</action>
<action>maincpu.pq@0C670008=890F50304015890C</action>
<action>maincpu.pb@0C670010=E0</action>
<action>maincpu.pb@0C670011=param</action>
<action>maincpu.pq@0C670012=E205302689026503</action>
<action>maincpu.pq@0C67001A=A0060009E0011302</action>
<action>maincpu.pq@0C670022=1303E005AFF70009</action>
<action>maincpu.pq@0C67002A=D001402B00090C1F</action>
<action>maincpu.pw@0C670032=00B0</action>
</script>
<script state="off">
<action>maincpu.pd@0C20254C=temp0 </action>
<action>maincpu.pq@0C670000=temp1 </action>
<action>maincpu.pq@0C670008=temp2 </action>
<action>maincpu.pq@0C670010=temp3 </action>
<action>maincpu.pq@0C670018=temp4 </action>
<action>maincpu.pq@0C670020=temp5 </action>
<action>maincpu.pq@0C670028=temp6 </action>
<action>maincpu.pd@0C670030=temp7 </action>
</script>
</cheat>
Last cheat to be found is, “Kill Bosses with 1 hit” cheat. As we know, while “Select Area to follow Area 1” cheat
research, we’ve noticed some bosses in game, middle bosses and Area bosses at the end of each area. After first
area, other bosses were complex as they transformed to other bosses when boss energy gauge reached to certain
positions. So, those will be tough to investigate. But the aim is, for each boss, there should be static memory
locations that hold default energy gauge value, and their transform. Those memory locations can be transferred to
other locations e.g. Area 2 boss is displayed. So, we’ll monitor boss gauge with ci and cn - commands, but we
need to find where that value comes from, initially. So if we change initial values, boss gauge indicator will appear
as the value we changed. For all of these, 6 save states need to be taken in MAME, for all Areas (Select Area to
follow Area 1 cheat can be used to start from Area 6). But it’s better those states are taken just before “Warning” is
displayed before bosses appears, because just before boss display, their energy gauge can be initted.
So, starting from Area 1 boss, loading that state, when boss energy gauge is filled to the maximum, ci ud to
initialize cheat (double word chosen because boss energy gauges are very big, they may be hold in double words).
And then, shooting at the boss and after some time, cn - command and cn eq command when no firing at boss,
result is: C7A3128 and C89322C (initial value 00078140) is found. (These memory locations can be different each
time loading state and searching cheat with this method, because as explained before, they’re copied from fixed
location but their location currently may vary.) So, we need to find this initial 78140 value, where it comes from at
first. When we search briefly, with loading state again and wp c7a3128,1,w, it stops at C2071A4, checking code
just before that:
26
0C207194: 5391 MOV.L @($04,R9),R3 ;R9=C52DEE4, copy long C52DEE8 to R3 (00078140)
...
0C2071A2: 1639 MOV.L R3,@($24,R6) ;copy R3 (00078140) to C7A3128. (dynamic)
Now Area 1 boss parameters are clear, let’s check original values:
C52DEE8: 00078140 ;it seems this is the “gauge display” value, not related with boss health.
C52DF08: 00000000 ;this value is read and compared, above routine.
So, for Area 1 boss, we need to change C52DF08 with 00078140 (max value), so when boss is hit, lower value is
compared with this static 00078140 in PC=C207450 and it will go on from C207454 (boss dead).
Same needs to be applied for other Area bosses. But you’ll notice that, other bosses have more parameters for
second gauge (one boss dies and transforms into another boss in same Area), after some hard work here is all of
boss tables revealed and ready for cheat format:
(* means progress bar value and rightmost values after “;” are their default values.)
Short explanation, i.e. Area 4 boss: Boss maximum health is at 000A2380 (read from 0C5377A4) and stored to
0C5377C4 (compare value taken from here). Two sub-bosses exist, their progress bar values found and cleared
(0C5377D4 and 0C5377E8). There’s a counter for these bosses (including sub-bosses that appear with transform)
and its default value is 00, increased by one and when a sub-boss dies. So, maximum number of bosses for that
Area is stored to 0C5377BC. It means, when cheat is on, after one single hit, first compare will result in “boss dead”
in routine above, and at second compare, sub-bosses check, as counter is modified to its maximum value, Area will
be clear immediately!
On next page, full cheat is displayed. Notice that, if temp variables are more than 10, a special tag should be used
as tempvariables=”XX”:
27
<cheat desc="Kill Bosses with 1 hit" tempvariables="17">
<script state="on">
<action>temp0 =maincpu.pd@0C52DF08</action>
<action>temp1 =maincpu.pd@0C52FE94</action>
<action>temp2 =maincpu.pd@0C52FEA4</action>
<action>temp3 =maincpu.pd@0C52FE8C</action>
<action>temp4 =maincpu.pd@0C534A78</action>
<action>temp5 =maincpu.pd@0C534A88</action>
<action>temp6 =maincpu.pd@0C534A9C</action>
<action>temp7 =maincpu.pd@0C534A70</action>
<action>temp8 =maincpu.pd@0C5377C4</action>
<action>temp9 =maincpu.pd@0C5377D4</action>
<action>temp10=maincpu.pd@0C5377E8</action>
<action>temp11=maincpu.pd@0C5377BC</action>
<action>temp12=maincpu.pd@0C53B7C0</action>
<action>temp13=maincpu.pd@0C53B7D0</action>
<action>temp14=maincpu.pd@0C53B7E4</action>
<action>temp15=maincpu.pd@0C53B7B8</action>
<action>temp16=maincpu.pd@0C53F600</action>
</script>
<script state="run">
<action>maincpu.pd@0C52DF08=00078140</action>
<action>maincpu.pd@0C52FE94=00083680</action>
<action>maincpu.pd@0C52FEA4=00000000</action>
<action>maincpu.pd@0C52FE8C=00000001</action>
<action>maincpu.pd@0C534A78=00091AE0</action>
<action>maincpu.pd@0C534A88=00000000</action>
<action>maincpu.pd@0C534A9C=00000000</action>
<action>maincpu.pd@0C534A70=00000002</action>
<action>maincpu.pd@0C5377C4=000A2380</action>
<action>maincpu.pd@0C5377D4=00000000</action>
<action>maincpu.pd@0C5377E8=00000000</action>
<action>maincpu.pd@0C5377BC=00000002</action>
<action>maincpu.pd@0C53B7C0=00100780</action>
<action>maincpu.pd@0C53B7D0=00000000</action>
<action>maincpu.pd@0C53B7E4=00000000</action>
<action>maincpu.pd@0C53B7B8=00000002</action>
<action>maincpu.pd@0C53F600=00122E80</action>
</script>
<script state="off">
<action>maincpu.pd@0C52DF08=temp0 </action>
<action>maincpu.pd@0C52FE94=temp1 </action>
<action>maincpu.pd@0C52FEA4=temp2 </action>
<action>maincpu.pd@0C52FE8C=temp3 </action>
<action>maincpu.pd@0C534A78=temp4 </action>
<action>maincpu.pd@0C534A88=temp5 </action>
<action>maincpu.pd@0C534A9C=temp6 </action>
<action>maincpu.pd@0C534A70=temp7 </action>
<action>maincpu.pd@0C5377C4=temp8 </action>
<action>maincpu.pd@0C5377D4=temp9 </action>
<action>maincpu.pd@0C5377E8=temp10</action>
<action>maincpu.pd@0C5377BC=temp11</action>
<action>maincpu.pd@0C53B7C0=temp12</action>
<action>maincpu.pd@0C53B7D0=temp13</action>
<action>maincpu.pd@0C53B7E4=temp14</action>
<action>maincpu.pd@0C53B7B8=temp15</action>
<action>maincpu.pd@0C53F600=temp16</action>
</script>
</cheat>
Final words: As an introduction, 7 cheats found and installed. It seems this game has more extra features and
hidden content, they can be examined later, using same methods explained here. Also, adapting these cheats for
previous revision [ddpdfk10] needed, but revision history should be checked, there might be some features missing
for previous release.
28
[vendetta] Vendetta (C) 1991 KONAMI
“Evil is lurking in Dead End City. In a turf war, the Dead End
Gang has kidnapped Kute Kate from the rivals, the Cobras.
The Cobras set out to save her and stop the expansion of
the Dead End Gang in Vendetta.
The Dead End Gang is looking to expand their territory, so
the Cobras must infiltrate every area around the city. First,
they must gain entrance to Dead End City. Relying on their
fighting skills, the Cobras face a multitude of enemies. …
Each player character resembles a real-life personality.
Hawk looks like 'Hulk Hogan', Sledge looks like 'Mr. T',
Boomer looks like 'Jean-Claude Van Damme' and Blood
looks like 'Mike Tyson'.”
#ci&cn #non-linearlevel #detectautomate
#endsequence
Goal: To find “Start from Stage” cheat, also, if player holds a weapon and then gets hit by enemies afterwards,
weapon is dropped on floor and vanishes in time, examine routines to avoid vanishing. See Endsequence cheat
may also be investigated.
Known facts: Stage numbers can be linear, as there are 5 main stages + sixth stage where all bosses appear. But,
as “Start from Stage” cheat is missing in cheat database, maybe it’s a bit complex. Weapon vanishing can be
investigated with built-in MAME debugger cheat search, as that vanishing progress period seems constant. For
Endsequence cheat, at first “Start from Stage” should be finalized and then extra parameters need to be collected.
OK, starting with stage value analysis, so after starting game, standart ci and cn +,1 method applied. After you
reach Stage 3, cheat is found as 2848 (Start 01 – Current 03). Time to check this memory location, after reset,
checking write operations with wp 2848,1,w and it will stop twice writing 0x01 to 2848, when we manually poke
0x03 into 2848 twice, what we notice is, only blinking “Stage 1” text became “Stage 3” on the screen, when stage
started. So, 2848 probably only keeps stage number to be printed on screen. So, there should be another byte
responsible of current stage, enemies of that stage, shortly, plot of each stage. This info can help us, while playing
Stage 1, once, player enters through a door and starts inside of the building, these also happen in each stages, so
there are sub-stages, that’s why, very first start of each stages (1 to 6) may have non-linear values. Then, it’s time
to start a new game and start a new cheat search with ci and cn + method this time, cn + after each stage starts (1
to 6 – not sub-stages) and result is, 2846 (Start 00 – Current 0B) when we reach Stage 4. This is worth a try, this
time, after reset, wp 2846,1,w and before character select screen appears, it’s triggered. Let’s manually poke 0x0B
into 2846 with MAME debugger monitor window and see the result: Blinking “Stage 1” appeared at first, but game
directly started from Stage 4! As a result, two memory locations found, one is linear (2848) and responsible for
stage number display, other one is non-linear (2846) and responsible for real stage data. Below table gathered by
playing all stages and noting down values from 2846 and 2848:
To finalize this cheat, some more research needed, as game starts and no coins are inserted, demo mode starts
after a minute, from Stage 1, with “GAME OVER” on screen to indicate it’s a demo game. So, directly poking into
2846 and 2848 when their values are 00 probably won’t work because when demo game starts, conditions are the
same, demo game will also start from what’s selected in cheat menu. So, real game specific parameters need to be
found. This may help:
While examining memory, from 4A00 and forward, it changes when current screen changes, so kind of tiles or
graphic data is displayed there. So, taking two bytes from there and using as conditions will help, so when demo
game starts, 4A00 and forward memory will be different so cheat won’t be applied. Choosing two bytes as:
4A50 = 0x25 and 4A6A = 0xE6 (when character select screen is displayed)
So, on next page final cheat can be examined. These two memory locations are used for conditions, all 6 stage
parameters are defined from 2846 non-linear values, and when this condition is true, cheat pokes proper value into
2846. Last step, checks condition again with (param) and pokes linear value into 2848. Also tested when demo
game starts, does not affect demo game at all.
29
<cheat desc="Select Starting Stage">
<comment>This cheat must be turned on before main character select
screen.</comment>
<parameter>
<item value="0x00">Stage 1</item>
<item value="0x03">Stage 2</item>
<item value="0x07">Stage 3</item>
<item value="0x0B">Stage 4</item>
<item value="0x0D">Stage 5</item>
<item value="0x10">Stage 6</item>
</parameter>
<script state="run">
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6)">maincpu.pb@2846=param</action>
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6) AND (param == 01)">maincpu.pb@2848=00</action>
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6) AND (param == 03)">maincpu.pb@2848=01</action>
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6) AND (param == 07)">maincpu.pb@2848=02</action>
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6) AND (param == 0B)">maincpu.pb@2848=03</action>
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6) AND (param == 0D)">maincpu.pb@2848=04</action>
<action condition="(maincpu.pb@4A50 == 25) AND
(maincpu.pb@4A6A == E6) AND (param == 10)">maincpu.pb@2848=05</action>
</script>
</cheat>
Next cheat to be found is preventing weapons vanish in time. While holding a weapon, allowing opponents to hit
us, and when weapon falls onto the floor, ci and cn - method: Around 6 cheats found (These may vary because
they’re mostly variable):
We need to monitor their values, where they reach zero, and on screen, weapon should be completely vanished
same time. Above, all of them loops, it means, decreases to zero and becomes 0xFF and decreases again. So
they’re eliminated. But when we examine 3286, it started from 0x12 and ended with 0x00 when we investigate to
the end. Afterwards, weapon is vanished. So, routine which causes 3286 value decrease needs to be found, to
avoid vanishing. Here is the routine, found by wp 3286,1,w
So, this “deca” instruction needs to be changed with NOP to avoid countdown.
1112: AE nop
But, if you try to install this cheat with <action>maincpu.mb@1112=AE</action>, it won’t work because CPU uses
bank switching, it means, maincpu region (0000-FFFF) is updated/changed several times with different routines.
Here is the proof: bp 1112 in MAME debugger, you’ll notice the code at 1112 is periodically changing (at least two
different routines are present), when debugging breaks at 1112.
Another proof is, if KONAMI CPU ‘:maincpu’ program space memory is active in new memory window (Ctrl + M),
memory from 0000 to FFFF is accessible, but if Region ‘:maincpu’ is selected, memory from 00000 to 47FFF is
available. The difference comes from data in all banks. Here is bank layout for this CPU, gathered with memdump
command. When this command is entered in MAME debugger, memory dump is saved into memdump.log file. In
that file, under “Device ':maincpu' program address space read handler dump” section:
00000000-00001FFF = 05: Bank ':bank1' [offset=00000000] ;each bank consists of 0x2000 bytes
00000000-00047FFF ;this was “whole” maincpu region
30
So, here is memory layout: (28 additional banks, 0x1C times 0x2000 = 0x38000)
Bank switching is a technique to extend memory access. As an example, for CPU systems with 16 bit address bus
(Z80, 6502 or 6809), addressable RAM/ROM region is = 65536 bytes (0x0000 to 0xFFFF). As some of these
systems have more than 64K memory, address space needs to be divided into a number of blocks and with bank
switching method, that extended memory can be dynamically mapped and accessed.
This means the code we found above (decrease value of A, store back to 3286) is inside one of these additional
banks and cheat should be installed in extended memory. Unfortunately MAME debugger does not support
extended search from 00000 to 47FFF, so only way is searching bytes from above routine, with hex editor. Before
that, these steps need to be completed:
- After pressing TAB key for MAME menu and under “Game information”, driver can be gathered as “vendetta.c”.
- As mamedev.org website has sources online, search of “mame vendetta.c” in browser will end up with online
source code in mamedev.org in text format as: http://mamedev.org/source/src/mame/drivers/vendetta.c.html
- After searching for “ROM_START” text in that web page, for [vendetta]:
So, we need to extract vendetta.zip MAME rom and open 081t1 file inside hex editor. The routine we found has this
sequence: 29 0F 63 01 8C (disassembled on previous page); so when we search for these bytes in hex editor, it’s
found at offset 3110E! As above ROM_LOAD command points 0x10000 for base address, it seems its real address
should be 10000+3110E = 4110E. For the last step, this needs to be checked in MAME debugger:
Ctrl + M for new memory window, from its dropdown list, Region ‘:maincpu’ is selected and checking 4110E and
forward, result is:
It seems original routine is inside the bank 40000-41FFF; finally, cheat is applied to region at 41112:
Now, as “Select Starting Stage” is found, we may take a chance, for jumping to End Sequence. For this purpose,
this info may help.
As valid for some games, there are special points in game, where an automatic progress is executed, as player’s
joystick controls might be disabled by code and automatic progress starts with automatic player movement, e.g. to
enter somewhere and proceed to next section. These are of course decided with conditional checks, for example,
checking if all enemies are dead in current section, or scrolling has ended and end of level is reached. If these
parameters can be found, by changing one or a few memory locations, this automated progress can be forced,
same time, level/stage parameters can be changed and skipping current level/stage or even directly jumping to
end sequence will be possible.
31
As this game already has this option, at Stage 1, after defeating first boss, players automatically enter through the
door and next section begins, we need to investigate exactly that point. If we can learn how it’s triggered, we can
manually trigger it with the cheat, same time, changing current stage parameters inside same cheat and we can
directly jump to end sequence.
So, at Stage 1, we reach that first boss (masked creature with green pants) and when boss has little energy, save
game state with Shift + F7. With some luck, you can catch this value:
- When we’re controlling our player, ci ub,2800,100 to initialize cheats for 2800 – 2900 area.
- After we kill the boss, and our movement is CPU controlled; cn +
- When new section starts, we’re controlling our player again; cn -
Found address is 2855. It becomes 0x01 when current section is cleared, automated progress starts and its value
is 0x00 when new section starts. Let’s check saved state, load it with F7, when normal game play is active,
manually poke 0x01 into 2855. You’ll notice that section will automatically be cleared!
But we need to find some more conditions to finalize this cheat, because it should only work in-game, should not be
executed in other screens as character select, demo game etc. So, 28XX region is checked with monitor, in all
conditions; normal game, demo game, character select… Here are special memory locations:
2803 (Its value is 0x02 only for normal game; otherwise its value is different.)
283C (Its value is 0x01 for normal game, in demo mode it’s 0x00.)
Above is the constructed cheat with those condition checks. It’s a Now! (one hit) cheat. If 283C is 0x01 and 2855 is
0x00 (player controlled) and 2803 is 0x02, when executed, temp0 variable is set to 1. And according to it is 1, it
means conditions are met, end sequence is ready to be executed with poking 0x01 into 2855 (auto movement –
current section finished) and 0x10 into 2846 (last Stage’s value).
Final words: After detailed testing all cheats above, applying cheats to clones [vendettaj], [vendetta2pd],
[vendetta2pu], [vendetta2p] and [vendettar] will be the last step.
32
[mk2] Mortal Kombat II (C) 1993 MIDWAY
#easteregg #adaptexisting
Known facts: It seems fighting VS Smoke has certain conditions to be fulfilled, so current [mk2] cheats, especially
RAM cheats can be examined and starting with monitoring those addresses might be a good start.
First one, Smoke battle analysis, it’s mentioned that current stage should be Portal Stage. This is from [mk2] select
Kombat Zone cheat:
So, let’s start a new 1UP game and check 1061AC0 with wp 1061ac0,8,r
(Taking width as 8 because each byte in TMS34010 CPU start from xxxxx00, xxxxx08 etc.)
It’s good that debugging stops at this watchpoint, after uppercutting opponents. But which routine is the one we’re
looking for? If we go on searching, execution of code stops at FF8DF040. Also we notice in MAME window, next
instruction is cmpi 7h, A0 (compare with 0x07) so it’s probably the routine we’re looking for. Here is disassembly:
As we examined the code, the routine starting a fight VS Smoke should be at FFA0BA10. Here is another routine
called from above: (We can manually poke 0x07 into 1061AC0 and apply bp ff8df070 to trace the subroutines
called after that.)
33
FF81E740: 5600 CLR A0 ;clear A0 register
FF81E750: 05A1 01F0 0106 MOVE @10601F0h,A1,0 ;get value of 106101F0, store into A1
FF81E780: 0D3F 0003 CALLR FF81E7D0h ;call routine below
FF81E7A0: 05A1 0DC0 0106 MOVE @1060DC0h,A1,0 ;get value of 1060DC0, store into A1
FF81E7D0: 0B41 FFFE CMPI 1h,A1 ;is it 0x01?
FF81E7F0: CB01 JRNE FF81E810h ;if not 0x01, return
FF81E800: 1020 INC A0 ;increase A0 register
FF81E810: 0960 RETS ;return back
Everything is clear now. Above routine should return with A0=0x02. For that, both 10601F0 and 1060DC0 should
be 0x01. 10601F0 is already 0x01, but when we check 1060DC0, it’s 0x00. It needs to be investigated. Here is
interesting result:
1060DC0 is 0x00 for an active 1UP game, 0x01 for an active 2UP game. So, by examining game code, we also
had this valuable info, it seems, fighting VS Smoke is only available in a 2UP game!
So, let’s check main code on previous page, apply manual poke (0x07 into 1061AC0) and start a 2UP game from
now on, and try bp ffa0ba10 to monitor execution of this routine. It will take much time, when PC reaches there,
guess what? Stopped at breakpoint, and then immediately Dan’s face appeared! So, thinking of codewise, the
subroutine at FF80EC20 is probably a randomizer routine or kind of minimizing chance routine, when it’s executed,
it’s like rolling dices and waiting for 6:6 to appear! So, if we directly jump to FFA0BA10, we’ll always notice Dan’s
face after an uppercut. So, original code is:
So, let’s check the cheat as it is, when we temporarily check this, we’ll notice that, Dan Forden’s face is appearing
every time with an uppercut, but immediately holding joystick down + pressing Start button afterwards, seems not
responding, Smoke fight never starts. There might be another check? Let’s see.
We start a new 2UP game, and making sure that current stage is not the Portal stage, above command in
debugger and then trace <filename> to start tracing, perform an uppercut, when Dan Forden’s face appears,
immediately joystick down + Start button, and trace off when all tasks are done. If we open trace file and search
“cmpi 7h” text, to find where 0x07 is compared (with current stage) and here is part of code:
FFA1A110: 05A0 1AC0 0106 MOVE @1061AC0h,A0,0 ;get current stage(1061AC0)&poke into A0
FFA1A140: 0B40 FFF8 CMPI 7h,A0 ;is it 0x07 Portal?
FFA1A160: CB00 0247 JRNE FFA1C5F0h ;if not equal to 0x07 branch end
So, there’s another stage check just after Dan’s face appears. We need to remove this condition check also:
FFA1A160: 09C0 0247 MOVI 247h,A0 ;changed JRNE with null MOVI so,
;branch never happens to the end
So, cheat is now finalized by changing 3 values:
34
<cheat desc="Fight VS Smoke easily">
<comment>After an uppercut, Dan Forden's face will always appear, press
DOWN+START to fight VS Smoke.</comment>
<script state="on">
<action>temp0=maincpu.mw@001BE00</action>
<action>temp1=maincpu.mb@001BE25</action>
<action>temp2=maincpu.mw@004342C</action>
</script>
<script state="run">
<action>maincpu.mw@001BE00=C011</action>
<action>maincpu.mb@001BE25=C0</action>
<action>maincpu.mw@004342C=09C0</action>
</script>
<script state="off">
<action>maincpu.mw@001BE00=temp0</action>
<action>maincpu.mb@001BE25=temp1</action>
<action>maincpu.mw@004342C=temp2</action>
</script>
</cheat>
Now, second cheat search, about Johnny Cage’s red shadow kick. We found that obstructive routine at FF80EC20
for Smoke cheat, maybe this same routine is executed to decide Johnny Cage’s shadow kick (default in green color
or very rarely, red)? So, it’s time to start a new 2UP game for test simplicity, and selecting Johnny Cage as Player
1. bp ff80ec20 command and performing shadow kick with Back, Forward, Low Kick. Yes, stopped at breakpoint,
but we’re inside of this subroutine. To reach main routine where this is called, pressing Shift + F11 to step out main
routine. It’s displayed on main debugger window now, as:
We notice that, if FF88F800 is reached, it means it’s always default yellow shadow kick. So, this JRNC should be
replaced with NOP, to remove that branch and allow code to go on from FF88F7D0 to perform a red shadow kick
every time:
This was easy, as we knew common routine and guessed it’s executed also here.
For last cheat investigation, stage fatality in Kombat Tomb Stage and holding joystick down for auto-fall from
spikes, let’s start a new 1UP game, we can use existing cheat to start from that stage, and save game state at final
round, when CPU opponent is about to die. OK, everything is ready, so we can kill CPU opponent and apply Stage
Fatality. When CPU opponent hit the spikes, immediately, holding joystick down and waiting… No luck, auto-fall
didn’t happen. But after we load state and repeat these steps, after several tries, auto-fall from spikes happens!
Maybe same obstructive routine is preventing auto-fall? It’s worth a try. So, loading state again, creating a
breakpoint with bp ff80ec20 and waiting it to be triggered. It may be triggered a few times, but most important
moment is, when CPU opponent is on spikes. When this happens, let’s check by pressing Shift + F11 to find from
where this routine is called, it’s FF8EB170. Here is the routine:
35
FF8EB150: 09C0 0032 MOVI 32h,A0
FF8EB170: 0D5F EC20 FF80 CALLA FF80EC20h ;obstructive routine called from here
FF8EB1A0: C925 JRNC FF8EB400h ;if Carry flag is not 1, branch FF8EB400
FF8EB1B0: 09C0 0080 MOVI 80h,A0
For final step, we press F11 (single step) when PC is at FF8EB1A0, it seems mostly Carry flag is 0 there and it
jumps to FF8EB400. Now it’s time to test what’s in our mind: When PC is at FF8EB170, just before executing
obstructive subroutine, poking 0x0300 (NOP) into FF8EB1A0 so that branch won’t happen. Like this (we can
remove previous breakpoint):
bp ff8eb170,1,{maincpu.mw@1d634=0300;g}
Yes, after Stage Fatality is performed in Kombat Tomb stage, knocked CPU opponent auto-falls from spikes!
Final words: As it seems we’ve completed our task, it’s time to test all these cheats briefly, in all game modes; after
these tests, we’re sure that for parent [mk2], these cheats are working perfectly. Now it’s time to check revision
history from history.dat:
“REV. 2.1 :
Smoke and Jade were added.
The Pit II/Kombat Tomb fatality was added.”
According to this info, we won’t apply Smoke and Kombat Tomb stage fatality cheat for [mk2r11], [mk2r14] and
[mk2r20] as they are previous revisions, there’s no support.
For last step, as fighting VS Smoke conditions were collected by examining code (it must be a 2UP game,
otherwise; even in the Portal stage, holding joystick down and pressing Start button won’t result in fighting VS
Smoke), this info needs to be contributed, so, I again contacted authors of http://www.arcade-history.com and
currently, this is in TIPS and TRICKS section of history.dat, for [mk2]:
“* Fight against Smoke : in a 2UP game, press Down+Start when Dan says 'Toasty' on The Portal stage.”
Last important note is, as you realized, this FF80EC20 “obstruction” routine in Mortal Kombat 2 has so much
potential, probably there are many rare occasions, secrets are waiting to be revealed.
36
[fatfursp] Fatal Fury Special (C) 1993 SNK
“Garou Densetsu Special was released in September
1993 in Japan.
The title of this game translates from Japanese as
'Legend of Hungry Wolf Special'.
This game is known outside Japan as 'Fatal Fury
Special'.
Things that changed in this particular game : * The
combo system has been improved, hit damage has been
lessened & the game speed has been increased. *
Newcomers in the game are Duck King, Tung Fu Rue,
Geese Howard & Ryo Sakazaki.”
Goal: As requested, explore player movement system (moves list) and exchange current “hard to perform” special
moves with an “easy to perform” one, for all characters. Also, end sequence can be investigated?
Known facts: Fatal Fury 3 game has “Always have Easy Supers” cheat.
So, assuming that these “Fatal Fury” series used similar code in each game, best method right now is starting from
[fatfury3] Easy Supers cheat. Here is cheat for [fatfury3]:
<action>maincpu.pb@10E001=01</action>
So much simple, we expected a huge list modifying all character’s super moves.
Probably, this is a test-byte, in some games; coders insert test bytes to perform some in-game operations directly,
by controlling if a test byte is set or not. Test byte can execute a macro, a complex routine or combined routines,
or change some settings. We all know that some of arcade games have DIP switches, right? As they’re hardware
based, of course their values (as bits or bytes) are also controlled from game’s RAM or ROM area. So, test bytes
can be categorized as “software” or “debug” DIP switches.
0839 000X 0010 E001 btst #$X, $10e001.l ;test Xth bit of 10E001
082D 000X 6001 btst #$X, ($6001,A5) ;test Xth bit of 10E001
As all stops are examined and it seems bit 0 is not checked, (we need to find the code checking rightmost bit, first
bit) searching all code may help as:
Found at 3164E and 44670. (When cheat is on, bp c7b0 applied to get those values.)
After examining both, the one at 44670 seems special one, can describe how movements are changed.
It’s getting clear. If cheat is off, A3 doesn’t change, stays as 583DC. But if cheat is on, A3 value is changed and
new value is 58414. Probably, these are start address of movement tables. Let’s check.
37
058414: 0000 0000 0005 8492 0005 8526 0005 85B6
058424: 0005 8642 0005 86CE 0005 876A 0005 880E
058434: 0005 889A 0005 8932 0005 89C6 0005 8A2A
058444: 0005 8A86 0005 8AFA
It seems they point some memory locations, 13 values for each. There might be 13 characters total? OK, we can
learn it later. Let’s pick one from list and compare data (chosen ones are in red color):
05844C: 0011 0005 8C4E 0005 8C60 0005 8C72 ;all normal tables start with 0011
058492: 0013 0005 8C4E 0005 8C60 0005 8C72 ;all cheat enabled tables start with 0013
Let’s dump data starting from 58C4E, as they’re static for both:
058C4E: 0000 0F02 000C 0F08 0C0F 040C F011 FF0B 0611
058C60: 0000 0F02 000C 0F08 0C0E 040C F00E FF0C 060E
Now it seems 0011 and 0013 may point number of entries on that table, cheat on one has 2 more tables, let’s
check what is added (We open memory window and compare 5844C and forward with 58492 and forward, here
are +2 added entries):
Now let’s check how they are read, as both start from 58C4E, wp 58c4e,1,r command: It stopped several times at
“How to play” screen. Let’s disable it and choose another fighter for PL1 (other than Terry) and check: If “any”
player is not Terry, it’s not triggered. So, this should be movement table for Terry. (As you noticed, Terry is default
player in this game.)
Finally, checking table again, starting from 0011, now full table (no cheat):
05844C: 0011 0005 8C4E 0005 8C60 0005 8C72 0005 ;it seems this table is for movements of
05845C: 8C84 0005 8B66 0005 8B78 0005 8B8A 0005 ;first character, Terry.
05846C: 8B9C 0005 8BAE 0005 8BC0 0005 8BD2 0005
05847C: 8BE4 0005 8BF6 0005 8C22 0005 8B3C 0005
05848C: 8B4A 0005 8B58 0013 ;until 0013
So, Terry’s movement data starts from 58B4A and ends just after 58C84 (mixed data on table). As first brief check
of all those tables, we notice that, at the end of each table, there’s a $FF byte followed by another byte. That can
be movement number, and $FF probably points end of that movement, so here are two examples:
058B66: 0000 0F02 000A 0F08 0A0F 040A F009 FF01 ;first entry, $FF and $01.
058C78: 0000 0F02 000A 0F08 0A0F 040A F00B FF02 ;second entry, $FF and $02.
There are $FF bytes on each, which might point end of command, we can assume these tables are read, but as
they’re sequential, if a movement is successfully performed, the counter should increase and read next byte, until
that $FF probably. First table is very small, maybe represents first special move of Terry? Let’s check:
Starting a new game with Terry as 1UP player, while this watchpoint is active, when we perform “Burn Knuckle”
with D, DB, B and Punch button A, it’s triggered. So this is first movement on the table.
As second entry is same as first, except that F00B word, it should be same movement with another button
probably. Yes, it’s “Strong Burn Knuckle” with D, DB, B and Punch button C, and triggered.
So, checking $FF byte for each character’s entire movement tables, setting a watchpoint for that byte and
performing their special moves one by one (in a 2UP game preferred, without annoyance) will result in finding all
equivalents of joystick special movements in ROM memory.
38
Now we can investigate same movement tables for [fatfursp]. But our first aim is, checking if a test-byte as in
[fatfury3] exists here, then, it will save us lots of work.
So, starting [fatfursp] and performing these searches, after game starts (identical words from [fatfury3]):
No luck. It seems there’s no test-byte present. So we need to find movement tables for each character and replace
“hard to perform” super moves with easy ones. To not to waste time, with wp 58c4e,1,r command in [fatfury3], it
stopped every time where Terry was present on screen. Checking again, here is the code causes stop there:
So, starting a new game in [fatfursp], when game starts, search is:
Found at several places. When examining them, with breakpoints, no proper stop happened.
After game starts and reaches, bp 2e8c4 to check result, it stops several times, A2 register needs to be checked
because movements are read from that register. As Terry VS Geese is fighting in “How to Play” mode, here are
values of A2 for each trigger:
038B1A: 0000 0F00 0000 0C0F 0406 0D00 0C0F 04FF ;this should be first movement, ends with
038B2A: FF00 ;FF00. It’s (D, DB, B, Button A)
...
038BB8: 0000 0F02 0000 0C0F 080C 0F04 0C0F 080C ;this seems the last movement, ends with
038BC8: 0F03 08F0 0FFF 0979 000 ;FF09. Super (desperation) move perhaps?
So, as we checked Fatal Fury Special game movement list, and characters:
When we test above with wp 38b28,1,r and wp 38bcd,1,r watchpoints (end of movements $FF byte), first one is
triggered after performing D, DB, B and Button A. Second one is desperation move, for sure, it’s triggered after
performing D, DB, B, DB, F and Buttons B+C (Power Geyser move).
39
So, we need to find all movement tables for each character (we’ll use existing cheat to fight VS Ryo to get his table)
and note down all memory location start points for 0000 xxxx xxxx xxxx …. FF09 sequence, because these are
desperation move start addresses.
Afterwards, we need to decide a “simple” movement to replace with all those “hard” desperation moves. After
examining all moves list for each character, Wolfgang’s default desperation move is Hold B, UF and A+C button, so
we can adjust it and change to B, F and A+C and apply for all characters. Here is result:
0F04 0000 0C0F 030C F00E FF09 ;code for Back, Forward and A+C
So, we’ll poke this sequence, 16 times for each character, to their exact movement locations (their FF09 entries will
be replaced). Of course, their current FF09 table lengths are all checked, as we’re inserting a shorter sequence
with 12 bytes above, it won’t result in overflow. For cheat in XML format, “run” state, this 12 bytes long data is
divided into two temp variables (temp32 and temp33) and they’re poked to exact location where desperation move
code starts for all 16 characters. Further tests performed and all characters are tested, their normal movements are
OK and all their desperation moves are same, Back, Forward and A+C. Example for Terry’s movement table:
038BBA: 0F02 0000 0C0F 080C 0F04 0C0F 080C 0F03 ;default Terry desperation move
038BCA: 08F0 0FFF 0979 0000 ;ends with FF09.
038BBA: 0F04 0000 0C0F 030C F00E FF09 ;code for Back, Forward and A+C
40
<action>maincpu.mq@038C52=temp32</action>
<action>maincpu.md@038C5A=temp33</action>
<action>maincpu.mq@038D00=temp32</action>
<action>maincpu.md@038D08=temp33</action>
<action>maincpu.mq@038D78=temp32</action>
<action>maincpu.md@038D80=temp33</action>
<action>maincpu.mq@038DFC=temp32</action>
<action>maincpu.md@038E04=temp33</action>
<action>maincpu.mq@038E66=temp32</action>
<action>maincpu.md@038E6E=temp33</action>
<action>maincpu.mq@038EEC=temp32</action>
<action>maincpu.md@038EF4=temp33</action>
<action>maincpu.mq@038F80=temp32</action>
<action>maincpu.md@038F88=temp33</action>
<action>maincpu.mq@039014=temp32</action>
<action>maincpu.md@03901C=temp33</action>
<action>maincpu.mq@0390A6=temp32</action>
<action>maincpu.md@0390AE=temp33</action>
<action>maincpu.mq@039142=temp32</action>
<action>maincpu.md@03914A=temp33</action>
<action>maincpu.mq@0391CA=temp32</action>
<action>maincpu.md@0391D2=temp33</action>
<action>maincpu.mq@03923E=temp32</action>
<action>maincpu.md@039246=temp33</action>
<action>maincpu.mq@0392D6=temp32</action>
<action>maincpu.md@0392DE=temp33</action>
<action>maincpu.mq@039390=temp32</action>
<action>maincpu.md@039398=temp33</action>
<action>maincpu.mq@039430=temp32</action>
<action>maincpu.md@039438=temp33</action>
</script>
<script state="off">
<action>maincpu.mq@038BBA=temp0 </action>
<action>maincpu.md@038BC2=temp1 </action>
<action>maincpu.mq@038C52=temp2 </action>
<action>maincpu.md@038C5A=temp3 </action>
<action>maincpu.mq@038D00=temp4 </action>
<action>maincpu.md@038D08=temp5 </action>
<action>maincpu.mq@038D78=temp6 </action>
<action>maincpu.md@038D80=temp7 </action>
<action>maincpu.mq@038DFC=temp8 </action>
<action>maincpu.md@038E04=temp9 </action>
<action>maincpu.mq@038E66=temp10</action>
<action>maincpu.md@038E6E=temp11</action>
<action>maincpu.mq@038EEC=temp12</action>
<action>maincpu.md@038EF4=temp13</action>
<action>maincpu.mq@038F80=temp14</action>
<action>maincpu.md@038F88=temp15</action>
<action>maincpu.mq@039014=temp16</action>
<action>maincpu.md@03901C=temp17</action>
<action>maincpu.mq@0390A6=temp18</action>
<action>maincpu.md@0390AE=temp19</action>
<action>maincpu.mq@039142=temp20</action>
<action>maincpu.md@03914A=temp21</action>
<action>maincpu.mq@0391CA=temp22</action>
<action>maincpu.md@0391D2=temp23</action>
<action>maincpu.mq@03923E=temp24</action>
<action>maincpu.md@039246=temp25</action>
<action>maincpu.mq@0392D6=temp26</action>
<action>maincpu.md@0392DE=temp27</action>
<action>maincpu.mq@039390=temp28</action>
<action>maincpu.md@039398=temp29</action>
<action>maincpu.mq@039430=temp30</action>
<action>maincpu.md@039438=temp31</action>
</script>
</cheat>
41
Also, we can investigate another cheat, reaching end sequence immediately? While playing, we notice that, in
cutscenes, stage number is mentioned like: “The 2nd stage”.
So, we need to check first, how many stage numbers are present? Shortly explaining, with ci & cn +,1 method, we
find that 10B2F3 holds current stage number. When we manually poke and check, when it becomes 0x11, end
sequence is displayed, end of all stages. Now, as this game also has an attract mode (demo game), we need to
find special memory addresses to use as conditions. We can narrow search area with 1000XXX. That block with
0x1000 bytes also covers existing RAM cheat memory locations. First, 100010 address is found. Its value is 49D8
for in-game (no demo game, no in “How to play” screen, no in character select menu …). Also, we find 1000034
address, and after testing, it seems its value can be 0x01, 0x02 and 0x03 due to current game mode (PL1 and/or
PL2 active in game or not).
And last step is, as we’ll install this cheat with automation method (selected player’s number of already won round
value will be 1, selected player’s energy will be set more than other player’s, and remaining timer will be set to a
few milliseconds). So, these will simulate “End of current Round”. Finally, current round number will be poked the
maximum value), we need to find where number of rounds won are stored. Again with ci & cn +,1 (also cn -,1)
method, 100058 is found. It holds number of rounds won by PL1. After finding equivalent memory locations for PL2
also, here is what we have:
Here is the cheat, finalized with above info (Cheat can only be executed if 100034=0x49D8 and first or second bit
of 100010 is 1, for PL1 and PL2 respectively):
Final words: Movements in these kinds of games can be retrieved by this method, also; if there’s any existing
movement cheats for a game, it can be reverse engineered and movement tables can be revealed. Also, see end
sequence cheat is a good example of simulating end of current stage; this method can be applied to similar
“Fighter” games, to access end sequence directly.
42
[mslugx] Metal Slug X (C) 1999 SNK
“Originally released to improve upon some of the problems
the previous Metal Slug game ("Metal Slug 2 - Super
Vehicle-001/II") had, most notably its notorious slowdown,
this update also implemented several changes to beef up
the gameplay, such as new enemies, a different end boss
layout and a number of new weapons and secrets. The
result is a better game and one of the finest chapters in this
series.”
#easteregg
Goal: While searching for “Select Starting Mission”, “Rapid Fire” and “DEBUG DIP” cheats, RAM region was briefly
examined via MAME debugger and stumbled upon below data. So, do they really exist in arcade version?
0C8FB4: 5049 4E20 504F 494E 5420 4154 5441 434B PIN POINT ATTACK
0C8FC5: 2053 5552 5649 5641 4C20 4154 5441 434B SURVIVAL ATTACK
Known facts: For PSX version of Metal Slug X, under Combat School, Pin Point and Survival Attack modes are
available.
So, where to start? After examining youtube videos of PSX version, it seems screen layout is similar to above.
Upper panel has “1UP” entry, with same character set, “TIME” and probably “PIN POINT ATTACK” text might be
printed on screen. By default, “1UP” text is always on screen so it’s time to investigate and find print text routine
(this routine is probably a shared one for all text displaying).
find 0,100000,”1UP”
Found at C913F and C9169. Setting up watchpoints for them with wp c913f,1,r and wp c9169,1,r commands, after
player select is complete, first watchpoint is triggered with PC=3D1BA. We need to find main routine which
initializes memory location to start printing on screen, so time to press Shift + F11 and investigate parent routines.
Applying it twice and investigating previous code, here is the result:
0C9126: 5C63 0B5C 7006 5C6E A5A6 A6A6 A6A6 A6AF \c.\p.\n........
0C9136: 5C6E 5C63 035C 7001 2031 5550 3D00 5C63 \n\c.\p. 1UP=.\c
It seems with some formatting codes (0x5C), “1UP” text is printed on screen and after “=” block ends with $00
code. What do we have in hand? “PIN POINT ATTACK” text starts from C8FB4, so there might be a similar print
routine using lea instruction code and 3D244 subroutine call around there. Here is search command for it:
0C8E68: 0C39 0000 0010 C2E7 cmpi.b #$0, $10c2e7.l ;10C2E7 is 0x00 ?
0C8E70: 6700 0036 beq $c8ea8 ;if so, branch end (rts)
...
0C8E86: 41FA 012C lea ($12c,PC), A0; ($c8fb4) ;A0 is C8FB4 by default
0C8E8A: 0C39 0001 0010 C2E7 cmpi.b #$1, $10c2e7.l ;if 10C2E7 is 0x01
0C8E92: 6700 0006 beq $c8e9a ;branch C8E9A
0C8E96: 41FA 012D lea ($12d,PC), A0; ($c8fc5) ;A0 is C8FC5
0C8E9A: 303C 719D move.w #$719d, D0
0C8E9E: 323C 0103 move.w #$103, D1
0C8EA2: 4EB9 0003 D244 jsr $3d244.l ;print text routine
0C8EA8: 4E75 rts
43
Initial test is, setting a breakpoint for C8E68 and checking if it’s always executed: bp c8e68
OK, stops at that breakpoint after player select, at very first start of Mission 1. So, what to do is, saving the game
state with Shift + F7 on player select screen, loading game state and manually poking 0x01 or 0x02 into 10C2E7
and examining the results.
When 10C2E7 is 0x01; “PIN POINT ATTACK” text appears below screen, also “TIME” is displayed. Game starts
with 3 lives. Unfortunately when we go on testing to the end, after all lives are lost, screen brightness change and
“FAULT!” is displayed with “RETRY” and “EXIT” options. “EXIT” ends with high scores display and “RETRY” option
gets stuck on same “FAULT!” message.
When 10C2E7 is 0x02, “SURVIVAL ATTACK” text appears below screen. Game starts with 3 lives. Unfortunately
after all lives are lost, game is stuck. No sprite movements, only music keeps on playing.
Here are additional tests performed, to get rid of bugs mentioned above:
Unfortunately results are the same. Below tests are performed for difference analysis compared to PSX version
and 10C2E7 read/write analysis:
In a conclusion, it seems these modes were “really” planned to be fully played in arcade version, but finalization
was somehow dropped so it’s missing in final state. Here is the cheat, mentioned with WIP:
Final words: When a hidden text string is located in memory region, it can be examined briefly with above methods
(watchpoint set, breakpoint set on shared print routine) or manually disassembling back & forward area to catch PC
indexed loading (i.e. lea (±$xxxx, PC), Ax). In this example; for [mslugx], preliminary code reading those text strings
is successfully revealed and activated with above cheat. But in some other games, those text strings may be
lacking code support, as in Shadow Dancer [shdancer]:
01E079: 5345 4C45 4354 2054 4845 2053 4345 4E45 SELECT THE SCENE
01E089: 0004 4604 524F 554E 4420 2020 2053 5441 ..F.ROUND STA
01E099: 4745 000B B606 434F 554E 5420 5550 2044 GE....COUNT UP D
01E0A9: 4F57 4E20 4259 2053 484F 5420 4A55 4D50 OWN BY SHOT JUMP
01E0B9: 2042 5554 544F 4E00 0CB6 0653 5441 5254 BUTTON....START
01E0C9: 2042 5920 4D41 4749 4320 4255 5454 4F4E BY MAGIC BUTTON
44
[sharrier] Space Harrier (C) 1985 SEGA
Rapid fire cheat: #rapidfire
- Started a trace just after firing, and trace file examined with getting unique instructions.
- Firing routine found at 7AF4. Routine examined, it seems code is checking 4th bit, if it’s set, goes end.
- As 1st bit of same memory location is always 1, one byte changed and allowed code to check 1st bit.
- This way, rapid fire became always active!
007AF4: 3039 0012 40E0 move.w $1240e0.l, D0 ;move 1204E0 value to D0 (>$10)
007AFA: 3200 move.w D0, D1 ;move D0 to D1
007AFC: 5341 subq.w #1, D1 ;decrease D1 by 1
007AFE: B340 eor.w D1, D0 ;11 XOR 10 = D0 becomes 01
007B00: 0800 0004 btst #$4, D0 ;test fourth bit of D0 (it’s 0 now)
007B04: 6700 00AA beq $7bb0 ;if equal to zero, branch 7BB0 (RTS)
007B08: 41F9 0004 0800 lea $40800.l, A0 ;here is our breakpoint (executed just
007B0E: 3039 0000 3290 move.w $3290.l, D0 ;after fire pressed – displays fire)
- Started a trace just after firing, and trace file examined with getting unique instructions.
- Firing subroutine found at 00BB. Routine examined, 43A0 and 43A1 is $EF when fire is pressed.
- After testing both, 43A1 holds value of joystick direction or fire pressed. 43A0 is the shadow byte used for
preventing rapid fire (code explained below).
- As these two bytes hold inverted values, cheat has to be installed as 10 | (maincpu.pb@43A0 BAND ~10)
because fourth bit of 43A0 should be always 1.
- To finalize cheat; same region examined and found that 43A3 is 0x00 when PL1 is active and 0x01 when PL2 is
active, so this condition was used.
45
[aof] Art of Fighting (C) 1992 SNK
Desperation move cheat: #adaptexisting
- It’s known that, for Ryo and Robert, desperation move can only be performed if energy is very low and spirit is
near full.
- Used existing cheats and inserted 2 watchpoints to monitor reading energy and spirit values:
- wp 1092cd,1,r and wp 109a4,1,r
- After they had been triggered, examined code. And changed compare values, to allow desperation move all time.
0139A8: 0C2E 0060 0224 cmpi.b #$60, ($224,A6) ;compares 0x60 with spirit value
0139AE: 6500 0012 bcs $139c2 ;if spirit >= 0x60 go end
0139B2: 302E 004C move.w ($4c,A6), D0 ;store energy into D0
0139B6: 0C40 0020 cmpi.w #$20, D0 ;compare it with 0x20
0139BA: 6200 0006 bhi $139c2 ;if > 0x20, go end
0139BE: 4EFA 00B4 jmp ($b4,PC); ($13a74) ;perform desperation move
0139A8: 0C2E 0000 0224 cmpi.b #$00, ($224,A6) ;compare with 0, any spirit value
;accepted.
0139B6: 0C40 0080 cmpi.w #$80, D0 ;0x80 is highest energy value, so
;compare result won’t be > 0x80.
- First, performed a linear cheat search with ci & cn +,1 for each new stage, no luck.
- And then, a non-linear cheat search with ci & cn + for each new stage, found at A38EC with increasing values, but
after examining from code, it seemed, stage number is stored as word, value starts from DF40 and memory
location is A38ED, for PL1. All values for each stage noted down. Also PL2 stage value is at A38EF.
- Condition search performed, all demo games were checked. Luckily, none of demo games starts from very first
stage, so there’s no need for this condition.
- Special byte sequences found in A38EF. If it’s 00FF, it means, no start animation for PL1 for very first start.
- Same byte sequence at A38FF. If it’s FFFF, it means no start animation for PL2.
- So, for first line of the cheat, when poke operation into A38ED is detected (DF40 value), selected stage’s value
poked into A38ED.
- Afterwards, A38EF checked. If it’s 0x0000 for PL1, 0x00FF stored to disable startup animation for PL1.
- End of stage investigated briefly, before it, trace started and after trace analysis, found that A38E6 becomes
0x00FF when stage ends (default value is 0x0000). Afterwards, stage number word is increased by 2 there.
- Found additional conditions: A391F is 0x0001 for PL1 active and 0x0101 for PL2 active. For demo game, A38E0
is responsible byte. It’s 0xFF in demo games.
- All these conditions joined, and cheat created as:
- If A931F is 0x0001 and A38E0 not equal to 0xFF, poke FF byte into A38E6. Calculate value (next stage value-2)
with temp variables, and store it as byte into A38ED (As all stages start from DF).
46
Rapid fire cheat: #rapidfire
- Started a trace just after firing, and trace file examined with getting unique instructions.
- Firing subroutine found at 0677. Routine examined, A390F and A3910 is $40 when Button B (fire) is pressed (bit
6 gets 1).
- As A3910 is shadow byte, cheat needs to be installed as 00 | (maincpu.pb@A3910 BAND ~40) because bit 6
should always be 0 for rapid fire.
- As in “Finish Current Stage Now!” cheat, A391F value is used as condition for determining active player.
47
<cheat desc="Finish Current Stage Now! PL1">
<script state="on">
<action>temp0=maincpu.pb@A38ED</action>
<action condition="(temp0 LT 4A)">temp1=48</action>
<action condition="(temp0 GE 4A) AND (temp0 LT 56)">temp1=54</action>
<action condition="(temp0 GE 56) AND (temp0 LT 62)">temp1=60</action>
<action condition="(temp0 GE 62) AND (temp0 LT 6E)">temp1=6C</action>
<action condition="(temp0 GE 6E) AND (temp0 LT 70)">temp1=6E</action>
<action condition="(temp0 GE 70) AND (temp0 LT 7E)">temp1=7C</action>
<action condition="(temp0 GE 7E)">temp1=84</action>
<action condition="(maincpu.pw@A391F == 0001) AND
(maincpu.pb@A38E0 != FF)">maincpu.pb@A38E6=FF</action>
<action condition="(maincpu.pw@A391F == 0001) AND
(maincpu.pb@A38E0 != FF)">maincpu.pb@A38ED=temp1</action>
</script>
</cheat>
<cheat desc="Finish Current Stage Now! PL2">
<script state="on">
<action>temp0=maincpu.pb@A38F3</action>
<action condition="(temp0 LT 4A)">temp1=48</action>
<action condition="(temp0 GE 4A) AND (temp0 LT 56)">temp1=54</action>
<action condition="(temp0 GE 56) AND (temp0 LT 62)">temp1=60</action>
<action condition="(temp0 GE 62) AND (temp0 LT 6E)">temp1=6C</action>
<action condition="(temp0 GE 6E) AND (temp0 LT 70)">temp1=6E</action>
<action condition="(temp0 GE 70) AND (temp0 LT 7E)">temp1=7C</action>
<action condition="(temp0 GE 7E)">temp1=84</action>
<action condition="(maincpu.pw@A391F == 0101) AND
(maincpu.pb@A38E0 != FF)">maincpu.pb@A38E6=FF</action>
<action condition="(maincpu.pw@A391F == 0101) AND
(maincpu.pb@A38E0 != FF)">maincpu.pb@A38F3=temp1</action>
</script>
</cheat>
- Checked value of 104D50 (level value) after completing last level, 8. It’s 0x0C during end sequence.
- Checked 104CB2 condition again, its value is 0x0000 before 3/7 lives appear. End sequence execution adjusted
to happen before life selection.
48
<cheat desc="See End Sequence">
<script state="run">
<action condition="(maincpu.pw@104CB2 == 0000) AND
(maincpu.pb@104D50 == 01)">maincpu.pb@104D50=0C</action>
</script>
</cheat>
- Linear cheat search performed, 101978 found (current level number – starts from 0).
- Level number increase and compare routines briefly examined. Second Loop and final level info gathered from
history.dat file. 10197A holds value of Second Loop.
- As it seemed inserting new code into current ROM area is impossible, hook method applied, “add” instruction
code changed with “jsr”. Found that free region at 98000, for custom code.
0013FC: 5279 0010 1978 addq.w #1, $101978.l ;after each level ends, increases
;level number.
0013FC: 4EB9 0009 8000 jsr $98000.l ;branch to custom code at 98000
98000 4A79 0010 197A test 10197A ;check second loop byte
6644 bne end ;if not zero, go end (original routine)
4A79 0010 1978 test 101978 ;check current level byte
663C bne end ;if not zero, go end (original routine)
103C 0001 move #01,D0 ;here, (param) is stored into D0
B03C 0001 cmp #01,D0 ;is it 1?
6732 beq end ;if equal to 1,go end (original routine)
B03C 0006 cmp #06,D0 ;check with 0x06
6D24 blt r1 ;if < 6, branch r1
B03C 0006 cmp #06,D0 ;if = 6
6718 beq r2 ;branch r2
B03C 0008 cmp #08,D0 ;check with 0x08
6606 bne r3 ;if != 8, it means it’s 7, branch r3
903C 0001 sub #01,D0 ;here, D0 is 8. Minus one and becomes 7.
600C bra r2 ;branch r2
r3 4279 0010 197A clr 10197A ;clear second loop byte
903C 0001 sub #01,D0 ;decrease value of D0
6006 bra r1 ;branch r1 to store into current level
r2 5279 0010 197A add #01,10197A ;set second loop on with 0x01 byte
r1 13C0 0010 1979 move D0,101979 ;poke D0 into current level
4E75 rts
end 5279 0010 1978 add #01,101978 ;here is original code, increases level
4E75 rts ;number by 1.
- When boss energy gauges appeared, performed ci ud & cn - method to find gauge memory locations.
- After some tries, found them easily:
- Boss energies are stored in 10098, as double word (100158 for level 6 & level 7 bosses).
- Calculation performed for current level, and poked double word 0 into appropriate locations (10098 or 100158).
49
<cheat desc="Select Level to follow Level 1">
<comment>Second Loop is new harder game starting from Level 1, Level 7 is final
stage with two bosses after Second Loop.</comment>
<parameter>
<item value="0x01">Level 2</item>
<item value="0x02">Level 3</item>
<item value="0x03">Level 4</item>
<item value="0x04">Level 5</item>
<item value="0x05">Level 6</item>
<item value="0x07">Second Loop</item>
<item value="0x06">Level 7 (Special)</item>
<item value="0x08">End Sequence</item>
</parameter>
<script state="on">
<action>temp0 =maincpu.md@13FC</action>
<action>temp1 =maincpu.mw@1400</action>
</script>
<script state="run">
<action>maincpu.md@13FC=4EB90009</action>
<action>maincpu.mw@1400=8000</action>
<action>maincpu.mq@98000=4A790010197A6644</action>
<action>maincpu.mq@98008=4A7900101978663C</action>
<action>maincpu.mw@98010=103C</action>
<action>maincpu.mb@98012=00</action>
<action>maincpu.mb@98013=param</action>
<action>maincpu.mq@98014=B03C00016732B03C</action>
<action>maincpu.mq@9801C=00066D24B03C0006</action>
<action>maincpu.mq@98024=6718B03C00086606</action>
<action>maincpu.mq@9802C=903C0001600C4279</action>
<action>maincpu.mq@98034=0010197A903C0001</action>
<action>maincpu.mq@9803C=600652790010197A</action>
<action>maincpu.mq@98044=13C0001019794E75</action>
<action>maincpu.mq@9804C=5279001019784E75</action>
</script>
<script state="off">
<action>maincpu.md@13FC=temp0 </action>
<action>maincpu.mw@1400=temp1 </action>
<action>maincpu.mq@98000=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98008=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98010=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98018=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98020=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98028=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98030=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98038=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98040=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98048=FFFFFFFFFFFFFFFF</action>
<action>maincpu.mq@98050=FFFFFFFFFFFFFFFF</action>
</script>
</cheat>
<cheat desc="Kill Boss with 1 hit Now!">
<comment>Set this cheat after any boss completely appears on screen.</comment>
<script state="on">
<action>temp0 =maincpu.pb@101979</action>
<action condition="(temp0 LE 04) OR (temp0 == 06)">
maincpu.pd@100198=00000000</action>
<action condition="(temp0 GE 05)">maincpu.pd@100158=00000000</action>
</script>
</cheat>
50
[le2] Lethal Enforcers II (C) 1994 KONAMI
Finish current section cheat: #detectautomate
- Memory location of current section was known, found before this cheat investigation (C00022).
- Added C00022 check as a condition that values 0x07 and 0x10 were not allowed (bonus stage values).
- Also, kill bosses with one hit cheat was found before this, and found that boss energy is stored in C013F8.
- End of a section examined, before it, trace started and after trace analysis, found that when C00002 becomes
0x0400, automated skip to next section happens.
- For the last condition, same memory region monitored and found that C00002 byte must be 0x03 for in-game (no
demo – not inside menus).
- Also second cheat line used for clearing boss energy (C013F8).
- All above info used (condition checks) and additionally, stored 0x16 (last section value) into C00022.
- As above, stored 0x0400 into C00002 to start automated process.
- End of a section examined, before it, trace started and after trace analysis, found that FFC498 becomes 0x1010
in that process.
- For condition check, FFC403 memory location found. Its value is only 0x01 in normal game. (No bonus stage, no
demo game etc.)
- Performed tests with directly storing 0x1010 into FFC498, realized that number of bombs remaining should be
zero and thrown stars shouldn’t be zero (if so, no star used big bonus points awarded).
- Found those two with ci & cn +,1 (and cn -,1) method that FFC706 has number of bombs remaining and FFC40F
has number of stars thrown in current section.
- Cheat prepared with FFC403 check, and poked appropriate values into FFC498, FFC706 and FFC40F.
- Used above condition, FFC403; but its value should be 0x02 in bonus game.
- End of a bonus stage examined, just before automation, trace started and after trace analysis, found that FFC452
becomes 0x01 at the end of any bonus stage (successful or not), and automatic process starts.
- Tested directly poking that value, but enemy movements went on, so another method tested, applied ci & cn -,1
for each killed enemy. Found at FFC60E, number of enemies remaining for bonus screen.
- Also analyzed a successful bonus stage ending and found that FFC450 becomes 0x01 for frozen enemies (no
more movement).
- Cheat prepared as above, poked appropriate values after FFC403 check.
51
<cheat desc="Finish Current Section Now!">
<comment>Proceeds to next Section. Works in both 1UP and 2UP game.</comment>
<script state="on">
<action condition="(maincpu.pb@FFC403 == 01)">
maincpu.pb@FFC706=00</action>
<action condition="(maincpu.pb@FFC403 == 01)">
maincpu.pb@FFC40F=01</action>
<action condition="(maincpu.pb@FFC403 == 01)">
maincpu.pw@FFC498=1010</action>
</script>
</cheat>
<cheat desc="Finish Bonus Stage Successfully Now!">
<comment>Works in both 1UP and 2UP game.</comment>
<script state="on">
<action condition="(maincpu.pb@FFC403 == 02)">
maincpu.pb@FFC60E=00</action>
<action condition="(maincpu.pb@FFC403 == 02)">
maincpu.pb@FFC452=01</action>
<action condition="(maincpu.pb@FFC403 == 02)">
maincpu.pb@FFC450=01</action>
</script>
</cheat>
- As known, Cody has special knife stab ability, can stab enemies without dropping knife, in short distances. This
can be applied to Guy and Haggar?
- On player select screen, ci and cn + (also cn -) applied after cursor is over next player, found that FF1525
becomes 0x02 for Cody, 0x04 for Haggar and by default 0x00 for Guy.
- Afterwards, monitored read operations from there, and found that when selection is done and fire button pressed,
its value is divided by two and stored in FF85E9 (0x00 Guy, 0x01 Cody and 0x02 Haggar).
- Another watchpoint created, to read from FF85E9. It’s read and written to FF857C, and this address holds current
PL1 character. Another watchpoint created to monitor reading from FF857C. Two routines triggered, while holding
knife, original code:
00BC6E: 0C2E 0001 0014 cmpi.b #$1, ($14,A6) ;A6+$14 is FF857C. Is its value 1 (Cody)?
00BC74: 6606 bne $bc7c ;if not 1, branch BC7C
00BCA4: 0C2E 0001 0014 cmpi.b #$1, ($14,A6) ;same as above, is it 1 (Cody)?
00BCAA: 6706 beq $bcb2 ;if 1, branch BCB2
52
Never drop weapons cheat: #ci&cn
- Predicted for holding weapons that, a memory location could be 0x00 when no weapons held, 0x01 for weapon
held. So, after a few ci, cn +,1 and cn -,1 tries, found that FF85B2 is 0x01 when a weapon is held.
- Added watchpoint for reading from FF85B2, but limited that watchpoint to catch the moments that weapon should
be dropped (due to different kinds of hits player can get).
- Found 3 routines for above, by testing all kinds of weapon drop cases. Noticed that they check FF85B2, if it’s
0x00, branch and do not clear it again, but if it’s not zero, clear it and other locations after those “beq” instruction
codes. So, they were replaced with “bra”. First one’s branch range increased, to bypass “clr” instruction codes.
Original code:
53
[theglad] The Gladiator (C) 2003 IGS
One hit kills cheat: #ci&cn
- “Kill bosses with one hit” cheat was already found (with ci uw and cn - method).
- When about to reach first boss, before the cutscene, saved game state. Added watchpoints for 3 memory
locations above, to learn when initial energy of boss is stored.
- In that example, 0x07D0 (2000) was stored into 18015C7C. PC=802259C.
- Examined that routine, before 802259C:
08022592: 5A51 LDRH R1, [R2, R1] ;get value of (R1+R2) and store into R1
08022594: 3060 ADD R0, 60 ;add 0x60 to R0
08022596: 2900 CMP R1, 00 ;R1 is zero?
08022598: D100 BNE 0802259c (00) ;if not zero, branch 802259C
0802259A: 2101 MOV R1, 01 ;store 0x01 into R1
0802259C: 8101 STRH R1, [R0, #008] ;store R1 into (R0+$08)
- When boss energy was set, R1 was 0x07D0 and R0+$08 was 18015C7C.
- Noticed that 802259A can store 0x01 for R1=health, but 8022598 branch needs to be disabled. (As it’s known that
this code is always executed for every new enemy appearing on screen, as patch is performed as above, all new
enemies will have 0x01 health values.)
- While additional game info research, found that at mode selection screen, pressing these buttons unlock hidden
features and modes:
- At mode selection screen, trace started and applied one of above codes, when unlock happened, trace stopped.
- Examined that trace file briefly, unique results method, as unlocking happens only once inside the trace.
- Found that just before unlock done, button codes read from PC=8037166 and 80371682.
- After read memory was checked, found that 0809ADXX ROM area is read, button codes. And each progress
ends with $FF byte, means button combination OK.
- Tested all 4 combinations, added breakpoint for that routine, collected start address of all combinations. Noticed
that 0x06 is code for B button, 0x07 is C and 0x08 is D.
- Applied one button codes for those 4; at their button code start location, poked 0X and FF bytes.
- Here they are, normally in reverse byte order, shown in normalized format, right side shows cheat applied:
(As ARM7 CPU is little-endian, changes applied in reverse byte order in cheat XML file.)
54
0809ADD6: 060706070607FF two boss at once 08FF button D
0809ADCF: 060707070706FF more bosses 07FF button C
0809ADC8: 080808060606FF ranking mode 06FF button B
0809ADDD: 080708060706FF more skills 06FF button B
- Just before fighting with an enemy, performed ci and after enemy is wounded, a few cn - commands to find
memory region of enemy health. Found routine at 42AE:
- Saved game states just before fighting with all kinds of enemies including bosses.
- Checked all setups of [bp+2Fh] addresses, their init values (initial enemy health) but unfortunately [bp+2Fh]
setups were over 10 different places in memory, so initializing all enemy health values to 0x00 method didn’t work.
- Checked indexed load address ds1:[bw+iy] those hold value of each hit, for all enemies, but indexed memory
locations were read from different locations (more than 10), so not suitable for cheat setup.
- Checked all read instances of [bp+2Fh], they’re compared with 0xFF to realize if enemy is dead, but they were
also found in 10+ memory locations.
- For last approach; found all similar subtraction routines as above, for all kinds of enemies including bosses (all
save states were loaded and health decrease routines, applied ci and cn -, all were noted). Total 7 routines found.
- All A&B register setups changed to load maximum value on them, so these values were subtracted from enemy
health values in next instruction code and one hit kills became available.
55
09195: 26 8A 87 C5 A8 mov al,ds1:[bw-573Bh] ;for Arnold vs flying blue enemies
0919A: 8B 7E 48 mov iy,[bp+48h]
0919D: 28 45 2F sub [iy+2Fh],al
- As V33 CPU is little-endian, above changes applied in reverse byte order, below:
56
Tips and tricks:
1. Learn all essential instruction codes: Move methods, arithmetic methods, comparing, test operations, flags,
which flags are affected after specific instruction codes:
3. To write a custom code, learn branch forward – backward and how it’s calculated: (These calculations vary for
different kinds of CPUs.)
4. Exercise above with MAME debugger: After game has started, open debugger and execute instruction codes
one by one with F11 key (step into) and check left side of debugger, registers and flags are there. When yellow PC
indicator is on a conditional branch instruction, try to guess if the branch will happen or not. Also check memory
locations and their values with Ctrl + M (memory window). Memory addresses can be read or written with different
kinds of addressing (immediate – direct – indirect – indexed – PC relative …), so learn the basics.
5. If you cannot find instruction code & operation code manual for a specific CPU: Use dasm command in MAME
debugger to disassemble partial or full ROM area to the file you specify. You will notice all kinds of instruction
codes and their operation codes there. Example for TMS34010:
6. To examine previous code when breakpoint/watchpoint you created is triggered and debugging stopped: Press
Ctrl + D and on that disassembly window, replace highlighted “curpc” entry with “curpc-10” or similar. Another
method is history command, it can be used to retrieve recently executed instruction codes as:
history 0,10 ;dumps last 16 instruction codes (for CPU 0) executed to console window
Also, if you need to examine parent routine (if current routine is a subroutine), press Shift + F11 to step out to one
level up.
57
7. Use extended wp command, for selective watchpoint triggering:
bp 770,1,d0!=0f ;will only be triggered if D0 register is not equal to 0x0F when PC is at 770.
9. Use extended bp command, to poke value into RAM/ROM region and change registers:
Important note for the first example: If that breakpoint is executed, even only once, that poke operation will be
permanent. Disabling/clearing breakpoint or even soft/hard reset won’t restore its original value.
10. Use find command selectively, to find code location or memory region with one shot:
find 0,100000,”INSERT” ;search for “INSERT” string, address range is 0 to 100000. Watchpoint
;(read) can be applied to found address(es), to find text displaying routine.
find 0,100000,w.6700,000a,0a41 ;search for 0x6700 followed by 0x000A and 0x0A41, same range.
;chosen a low distance beq + eor operation codes, for less search results.
find 0,100000,7E,?,14 ;search for three byte sequence starting with 0x7E and ending with 0x14.
;wildcard search used with “?”
pc=150a ;perform direct jump to 150A and go on execution, for the active CPU
12. Instant poke into RAM/ROM inside MAME debugger: As above extended bp command is not always handy,
here is alternative way for direct poke operation (For ROM cheats, command must always start with “r”):
After applying direct pokes above, they can be checked from disassembly (Ctrl + D) or monitor (Ctrl + M).
58
13. Trace file, remove duplicate lines:
To simplify the trace file, this is the first method. All duplicate lines will be removed and sort operation will be
applied, so memory addresses executed will be sorted from low to high. It means if a specific code is executed
several times in that trace, it will appear only once.
Unique instructions in a trace file are executed code lines those were executed ONLY ONCE. So, if a code in a
memory location is executed more that once, all entries of that memory location should be cleared. After this
operation, new trace file will be very simple and all lines will refer to code those were executed only once. It helps a
lot for special analysis, i.e. after clearing a level/section in a game, increasing current level/section value process is
executed only once, so that part of the code will appear in final trace file.
- In Windows OS, to be able to execute UN*X commands, GNU utilities for Win32 is required.
- Download GNU utilities from http://unxutils.sourceforge.net/, named UnxUpdates.zip.
- Extract the zip file into a new folder.
- Copy original trace file to that folder, i.e. 1.txt.
- Open Command Prompt window, navigate to that folder where your trace file, uniq.exe and sort.exe files are in.
- Create a batch file in a text editor, save the file as unique.bat and put below commands inside it with copy/paste:
- After executing unique.bat, 2.txt file will be generated. It has only unique instructions inside, and it’s already
sorted.
59
World Wide Web links:
MAME cheat database related:
Useful software:
http://notepad-plus-plus.org/download/ Notepad++
60
GLOSSARY:
Big-endian: A computer architecture in which leftmost byte of a multi-byte string is addressed in
the lowest memory cell and other bytes are stored from left to right as memory cell
increases. For 68000 CPU, 0x42680006 is stored in memory as 42 68 00 06.
Debugger: It's a computer program used for testing and debugging target programs. Most
debuggers provide running a program in single stepping mode, pausing the
program, allowing breakpoints and tracking values of registers and flags.
DIP switch: It's a manual electric switch designed to be used on printed circuit board (PCB).
They were mostly used in arcade systems to store settings before the advent of
battery-backed RAM.
Flag: An off/on indicator that signals some condition. (Negative, Zero, Carry, Overflow,
Decimal flags are examples of most common flags.)
Instruction code: A group of bits that instruct CPU to perform a specific operation. Its format is:
Operation code + operand(s) or only operation code itself
For 68000 CPU, 0x0C400003 expresses cmpi.w #$3, D0
0x0C40 is operation code, cmpi.w #$XXXX, D0
It states one operand (0x0003), which is a word.
Operation code (opcode): It's the main part of instruction code, a group of bits that specifies the operation to
be performed.
Poke: BASIC command to set contents of a memory address. Additionally, for many 8-bit
computers; it was a common practice that just after loading games into memory or
freezing game with cartridge hardware, some modifications (i.e. unlimited lives)
were performed with POKE statements.
Signed number: A number that holds a value may be either positive or negative. Usually, most
significant bit (leftmost) states if a number is positive or negative. In 16 bit systems,
signed number range is from -32768 to 32767.
Subroutine: A set of instructions that can be called up by another program. A subroutine ends
with return (branch back) command, so control is transferred to main program.
Unsigned number: A number that cannot have negative value. In 16 bit systems, unsigned number
range is from 0 to 65535.
XML: Extensible Markup Language. It's a textual data format with Unicode, processor,
application, markup, content, tag, attribute and element support. MAME uses this
format to read cheats from <gamename>.xml file.
61