• Post
  • Reply
Zamujasa
Oct 26, 2010



Bread Liar
i'm a co-founder of the cutting room floor, and naturally one of my interests is going through nes games with a disassembler to see how they work, or if they have any interesting unused goodies in them, or whatever. sometimes i find things that are weird, stupid, funny, or clever, and mostly i just dump them on twitter

i'm going to try posting the dumb shit i find here instead, along with maybe some commentary on what's going on.

:eng101: if you want to share this thread with anyone, i maintain a hastily and infrequently updated public archive of it here: https://xkeeper.net/private/nes-thread.html

:siren: if you want to ask me shit about this or have any questions feel free to pm me or whatever, no promises i'll ever reply or read it but i can try to explain shit if you want



as an explainer, i typically disassemble nes games by these rough steps:

1. is there a tool-assisted speedrun of it? a tas that doesn't abuse severe glitches and plays in fceux is a godsend, because it means i can let someone else play the game, for...

2. take a "code-data log" (CDL) of the game. in short, fceux, my nes emulator of choice, has a feature that marks parts of the game as either code or data (hence the name). nes roms have all of their code and data mushed together, as this was before the era of embedded filesystems. a code data log thus lets us know that this chunk of ROM is code, and this chunk is data. you can find this out manually by following the code, but a CDL speeds things up a bunch. it can also help highlight things that are never read or run by the game, which might be unused or leftover content. here's a cdl window for the game i'm going to talk about in this post, king's knight, a janky famicom/nes game.



i've been using savestates and a max health cheat to play through the game, since i'm not good at it and quite frankly king's knight kind of sucks. but from this screenshot:

- 37.20% of the ROM has been identified as code
- 41.93% of the ROM has been identified as data
- 21.20% of the ROM hasn't been accessed one way or another

i haven't finished the game and i've missed a bunch of shit, but that's fine. without a tas or pre-recorded input movie, i usually don't bother playing through the full game if it's complicated like this.


3. with a decent-completion cdl in place, i then pop it into a disassembler, in this case a very legit, legal copy of IDA. i have a handful of scripts that rename the NES's various registers and provide some helpful enums like button mappings, then i use a script developed by some russian guy whose name i can never remember for the life of me. i think you pronounce it 'sanchez', though. this guy's real good, btw

here's a screenshot from ida:


the top bar is the general seek thing. gray is unidentified, red/blue are code, green is data. you can see some missing chunks that likely belong to one of the underground sections and the final level. (E: the large gray sections on the left are the NES's memory, $0000-$7FFF. the vast majority of it isn't usable for anything and stays gray forever)

in the middle of the screen is the NMI routine for this game. nmi is a little complicated to explain here but it runs pretty much every frame and is the start of very important cpu time on the nes, before the console starts actually drawing the screen.


4. mostly just look around for interesting structures, tables, and play with RAM editing the game based on what i see. for example, my cheat search turned up $009C as the player's health value, so i can look for things that modify the player's health to find things like life ups, damage routines, etc. one of my favorite versions of this is finding a "change music track" command, because a lot of the time a game's music is indicative of what's going on (power up jingles, title / game over music, etc). being able to identify the "change song" command + the values that are used (tracks, sfx, etc) can often really pop games open



anyway i'm stoned, adhd, depressed, etc, all of this was just so i could explain this dumb shit i ran into

https://twitter.com/xkeepah/status/1535570125801213952

this is an IRQ routine, used for when the NES CPU hits a BRK opcode or in some other cases like mapper scanline interrupts or the like.

most games that don't use IRQs either point it to the RESET routine (that runs when the CPU is reset), or to a simple "RTI" (return from interrupt).

this game has what appears to be boilerplate code that saves the state of the A, X, and Y registers, and then immediately restores that state. in short, it does nothing. but it wastes some time doing nothing that implies that, maybe at some point, it did something. or maybe it was just some example code with "put your code in here!" that never had code to go there. who knows.

nes games are stupid.

Zamujasa has a new favorite as of 18:26 on Jun 23, 2022

Zamujasa
Oct 26, 2010



Bread Liar
some other useful information to have handy while doing this:

- "data crystal" is a romhacking.net wiki. it's not terribly good but sometimes it'll have useful information. for king's knight, it happens to have a RAM map that is fairly spartan but helpfully gives us some of the player's current stats.

- walkthroughs/guides. gamefaqs to the rescue. this helps explain some stuff we'll probably find later, like the capacity for upgrades, what types of items exist, and level maps.

- nesdev, useful for remembering what values do what things for what registers.



(15 minutes of leaving this reply open later)

i'm not really sure why, but this game has a huge amount of apparently pointless jumps. all of these addresses are just a "JMP $xxxx" (to the second address in the list). several of these are subroutines that return with an RTS, so there's zero benefit whatsoever over just jumping to the routine you want rather than these weird intermediaries.

code:
B160 -> 8E31
B163 -> 96A4
B166 -> 9703
B169 -> B118
B16C -> 96C1
B16F -> 9721
B172 -> 8E89
B175 -> A28E
B178 -> B1C0
B17B -> BCFD
B17E -> BBFC
B181 -> BB91 (WritePPUAndScroll)
B184 -> B90A
B187 -> B911
B18A -> B37E
B18D -> B6C0
B190 -> B661
B193 -> B447
B196 -> B5BD
B199 -> B519
B19C -> B528
B19F -> B52C
B1A2 -> B54E
B1A5 -> B572
B1A8 -> B593
B1AB -> BF02
B1AE -> B412
B1B1 -> BBA3
B1B4 -> BEE4
B1B7 -> B3AE
B1BA -> BBBB
B1BD -> BBE6
one or two of these don't seem to be referenced anywhere.

Zamujasa
Oct 26, 2010



Bread Liar
It's fun being able to verify things like this:

quote:

Character Abilities and Levels

Each character must gather items to improve his abilities and levels and
cast spells. The items include as many as seven Jump increases, seven
Speed increases, three Weapon increases, and three Shield increases.
Each time one of these items is collected, the character gains a level.
Therefore the highest level is 20. But each character also has unique
abilities as listed below:

Knight "Ray Jack" is naturally quick moving, starting with a speed
increase
at level 1. With four pieces of Element D he can cast Zainen
to change into a winged horse. He is also somewhat good at defeating
each type of statue.

Wizard "Kaliva" is naturally a good jumper, starting with a jump
increase at level 1
. With four pieces of Element A he can cast Naizath
to defeat enemies in the water. He also defeats lion statues easily.

Monster "Barusa" is naturally shielded with defense, starting with a
shield increase at level 1
. With four pieces of Element C he can cast
Cetune to change into a dragon. He also defeats whole dragon statues
easily.

Thief "Toby" is naturally speedy, starting with a speed increase at
level 1
. With four pieces of Element B he can cast Balbath to destroy
monoliths. He also defeats gargoyle statues easily.

Here is the relevant code:




Basically, it resets your health to 12 HP, resets all of your stats, then (depending on which stage it is / which character you're playing as), gives you a free point in one of the stats.


I also found the music code, though unfortunately it's the kind of game where playing music requires calling a subroutine, rather than the music engine monitoring RAM addresses for tracks to play.



----

I also found what appears to be an undocumented (?) cheat, helpfully posted by some YouTube rando:



This game's stages work like this:

Stage 0: Overworld / Rayjack
Stage 1: Overworld / Kaliva
Stage 2: Overworld / Barusa
Stage 3: Overworld / Toby
Stage 4: Final stage
Stage 5: Underworld / Rayjack
Stage 6: Underworld / Kaliva
Stage 7: Underworld / Barusa
Stage 8: Underworld / Toby
Stage 9: Underworld / Toby..?

The GameFAQs guide doesn't mention an alternate map, the map for Toby's stage only shows one underworld area, and neither tcrf nor cah4e3's site mention it.



Huh.

The shorter dungeon feels like a glitch, almost, because there are a small handful of powerup items and they seem to be out in the open instead of hidden under destroyable objects. Weird!


E: strategywiki mentions it as an aside, but not in their actual secrets/codes section. :thunk:

Zamujasa has a new favorite as of 05:40 on Jun 11, 2022

Zamujasa
Oct 26, 2010



Bread Liar
one of the handier tools in the kit is djinn tile mapper, which is another one of those weird russian programs. it basically allows you to visualize the bytes of a file as graphical representations, generally from tiles in the rom.

for simpler games that don't do a lot of graphics bankswitching, you can usually find a bank of graphics that looks useful and then try to find something in the rom that shows up.




to represent this in the disassembly, i made an enum to translate the numbers into something resembling letters.





i also poked at the music format some but haven't really gotten anywhere and don't care too much at the moment.

there are 10 song entries, 0 through 9:




interestingly there are two "copies" of the death music, or rather two entries point to the same data. this is :speculate: but it's possible they intended to have a game over theme in addition to the one used for when you die. there doesn't seem to be any calls that would play the copy, though.






strangely, when looking for calls to play music, i found two places that appears to call an invalid song, one of which is below:



i have no idea if this code ever runs and i don't understand enough of it to figure out what's going on yet.
there is no song #$A (10), there are only 0-9. trying to play this song crashes the game because it reads garbage data for music pointers.



2022-06-13 update: i tried to force the conditions that would cause that to run, but it doesn't crash. my guess is that something else changes the music right after, or maybe something else happens, who knows. i haven't bothered looking into it yet

Zamujasa has a new favorite as of 05:45 on Jun 13, 2022

Zamujasa
Oct 26, 2010



Bread Liar
i found the code that saves and loads your character stats. during the first four stages, you play as only one character and their stats need to be saved somewhere when you aren't using them.



one thing this made me realize is that the character area isn't six bytes, it's seven, and the starting byte is before your health. oh well.

i had no idea what that other byte was for and hadn't looked into it much... but i did want to show off another example of dumb bullshit:


meet lua scripting!



fceux and a handful of other mostly not-great emulators have support for running lua scripts, that have access to the game's memory, drawing to the screen, and some other crap. visualizing what's going on can make figuring things out a lot easier. you aren't having to watch a memory viewer or trying to pop a breakpoint or whatever.

i just drew the 7 stats to the screen for each character and noticed that some of my savestates had a "1" for the unknown value instead of 0. turns out: those characters are dead. :rip:

in retrospect it makes sense, but they also could've just checked if that character's hp was 0 or not, maybe. who knows



lua scripting can be a fun thing for just getting to see stuff in a way you don't normally get. for example, here's chip n dale rescue rangers, but with an overlay showing the collision type of every tile:



black boxes are fully solid, yellow are "semi-solid" that you can jump up / fall through, white are flowers / crates, red/pink are spikes and other damaging objects, etc...

this reveals some fun things:



even though they look the same, the cables next to the pole can be dropped down through, while the cables to the left are solid. that way, you don't accidentally drop through them to your death.


here's the script for kings knight so far (it isn't much other than the above), but you can see how it works, sort of. i guess. i dunno, i didn't really comment it or anything yet

Zamujasa
Oct 26, 2010



Bread Liar
spent some time streaming disassembling the game some more, as well as some work on making a fun script for it.



it's not really useful for actual playing, but it shows the status of your party, along with the current player's stats. they aren't updated until the end of the stage / you die, though.



i also disassembled some stuff for the final stage of the game. all four characters are assembled into one party.

for the final stage, your stats are the minimum of each stat across all characters. if your jump stats are 7 7 7 0, your final party's jump stat will be 0. in addition, your health is basically averaged out, so if your party is injured you'll start the final stage with less health.

it's kind of rude, really.


----


on that note, when the game starts up, it checks if it's from reset or power-on by reading some values from memory; after initialization, it writes those values. this initialization also sets a flag that prevents you from opening the continue menu, and the code that starts the final stage clears that flag.

the data the game uses to track if it was reset:
"10 E8 64 0A 01 27 03 00"

It seems random, but I can't help but feel there's some significance behind it.




----


pointer tables on the nes tend to come in three different formats:

the "call a subroutine that reads the table address off the stack", useful if you have a lot of jump tables and don't care about time

code:
LDA [index to jump to]
JSR JumpTable

.word CodeFor0
.word CodeFor1
then also the split kind
code:
LDX IndexToJumpTo
LDA PointerTableHi, X
STA TempAddr+1
LDA PointerTableLo, X
STA TempAddr
JMP (TempAddr)

PointerTableHi:
  .byte high8(CodeFor0)
  .byte high8(CodeFor1)

PointerTableLo:
  .byte low8(CodeFor0)
  .byte low8(CodeFor1)
basically, splitting them in half. the order of the halves doesn't matter, and they don't need to be adjacent to one another, either. but usually they're fairly consistent on a game by game basis.


most of this game's tables are in hi, lo format. i just ran into one that was in lo, hi. dunno why.

Zamujasa
Oct 26, 2010



Bread Liar
i got some feedback! :toot: i'm glad people enjoy reading this nonsense; it's less instant than twitter but being able to explain shit without trying to squash it into 280 characters is nice. anyway, i didn't really feel up to doing anything else on king's knight today. so it goes. do want to keep making posts in here until i burn myself out for a few months again, so it's time for...

the first teenage mutant ninja turtles on the nes! it's not a great game.


i mostly did stuff with this a few weeks ago. let's start with the lua script i made:



this one's pretty complicated, so i'm going to break it down. sorry for the big image.



what does all this mean? i'll start from the top and go down.

CPU usage meters
basically, the game has two wait loops: one where it's waiting for the sprite zero hit flag to clear, and one where it's waiting for the sprite zero hit flag to be set.
a red bar represents the first wait; a green bar represents the second. more details on this below.


area, enemy set, rank, timers
  • "area" is just what, well, area you're in. think of it like smb1 worlds. there are 6 areas (shown here as 0 through 5). the number after the dash is the sub-area, and the number in parenthesis is just the sub-area value in hex. for example, every building you go into is a different sub area. the area number is shown on the pause screen, though that count starts from one (1~6).
  • "enemy set" is the types of enemies that will randomly spawn. there are three (four? uh) types of enemies per set; a loose guess is that there's a smaller type, a larger type, and then a flying type. i haven't actually looked into enemy types very much.
  • "rank" ("Rn") is the soft-focus of this post later on; here, the term is borrowed from shmups. your rank determines which enemy set is used, based on the area you're in.
  • the timer is... well, a timer. every non-lag frame, the second number increases; when it hits 00 or 80, the first number increases. normally this would mean that the big number goes up roughly every two seconds, but this game lags enough that you could probably call it three instead.


active objects
this is a list of object slots and what 'characteristics' it has. the 'characteristics' term is loosely based from the tasvideos documentation.
a lot of games typically assign some kind of type to an object; say, type 1 is a goomba, type 2 is a green shell koopa. type 3 might be a red one, or the game might use a subtype so that type 2-0 is a green shell, and type 2-1 is a red shell. this game... i don't know, honestly. there are a lot of potential identifiers and none of them make sense. thanks konami.

slot 0 is always you, the player!
slot 1 is for your weapon.
slots 2~4 are for your special weapons (e.g. boomerangs)
slots 5~F are for other enemies and the like. tasvideos says that slots D/E/F aren't used, but they are used for special objects in cutscenes sometimes; e.g. in the first level, the tied-up april in the last building is put in to slot F.

cutscenes and such use these objects and slots as well, e.g. the turtles entering the blimp or shredder's hands in the scene where you find splinter got ratnapped are thrown in.


health bar / object id, etc
the white cross (+) indicates the x/y position of the object directly from memory. this doesn't always line up with where the sprite actually is or its hitbox, obviously, but...
the slot and id are the same as above.
there's also a health bar, which is based on the enemy's current health (and loosely tracked by the script). the bars get longer with higher max HP up to a (very small) point.


current health
it's your health! :wth: every turtle has a maximum of #$80 (128) hit points, represented by 8 blocks. this just shows the full value in a slightly more visually appealing bar, i guess.
notably it doesn't work for enemies that have a health bar, which i haven't found what controls that yet. (i also haven't found the invincibility / iframe timers, oops. oh well)


high score, score, area score
remember earlier, when i mentioned datacrystal? if they have info, i'll often use it to speed up learning a game. in this case, they have a page on this game, and in its list of memory addresses is this:

quote:

C0 Score 99xxxx0
C1 Score xx99xx0
C2 Score xxxx990
C3 Unknown Score 99xxxx0
C4 Unknown Score xx99xx0
C5 Unknown Score xxxx990

C6 High Score 99xxxx0
C7 High Score xx99xx0
C8 High Score xxxx990
the unknown score here fooled me for a while, because it typically follows your real score. if you watch its value and kill some stuff, it'll go up just like your real score... so i just took this at its word and figured it was an unused, duplicate score.

turns out i was a big stupid idiot and wrong. not only did i initially mis-label by two bytes them in my disassembly (oops), the unknown score is used, after all. i guess i'll put more detail about it in the section on rank below, but in any case the game normally only shows your score and the high score. the area score is completely invisible.

any time the values change, the script will print out how much it changed by:

(enemy scoring isn't even consistent; one particular enemy will give 200 or 300 points depending on how it's spawned???)


idle cpu meter
this game seems to run its code entirely based on when NMI hits. NMI is a "non maskable interrupt", and on the NES, it's used to signal that the ppu is done rendering video and entering vblank, when you're allowed to make (safe) PPU updates.

basically, think of it like two threads: one is always running, and one is started when NMI/vblank hits and typically ends when an RTI (return from interrupt) is encountered. here's a simple-ish ascii chart:
code:
main thread      nmi thread
-----------      -----------
spin rng
spin rng
spin rng
spin rng
   .             check if nmi was running
   .             play music
   .             write ppu updates
   .             update joypads, etc            (vblank is probably over now)
   .             run game logic
   .             etc, etc, etc
   .             finish game logic + return
spin rng
spin rng
spin rng
these aren't real threads, but eh. anyway, the "spin rng" part isn't a joke, that's actually this game's idle loop, and it's what the blue bar at the bottom represents: how many times the game spun the rng, per frame.



on the overworld and elsewhere, the idle loop tends to run very frequently. in a side-scrolling area, the idle loop runs about 100 times per frame, with the rest of the time spend waiting for the sprite zero checks above (to handle rendering the status bar properly, and is why it glitches out when the game lags).


...

right, what the fuck was i going to talk about again? shit. remind me to call my doctor about adhd meds. fuck, is that the sun rising?


-------


soooo, about this game's rank system. rank is based off of three two things: your area score, the timer, and your health.

your area score is cleared every time you enter a new sub-area (e.g. going through a door or sewer hole). all points scored increment it as well... but there's a bug: the highest byte of your area score is sometimes increased when it shouldn't be. some enemies that award 1,000 points will award 101,000 area points instead. :thumbsup: the good news is that the high byte is never actually read anywhere so it doesn't matter.

when the game wants to recalculate your rank, it compares your area score against a list of thresholds:
- 2000
- 4000
- 7000
- 10000
- 14000
- 20000
- 28000
- 35000

if your area score is under the lowest threshold, the game doesn't bother changing anything; your "rank" is 0.

otherwise, the highest threshold you've reached is used to check the timer. based on your area score, your current timer is checked against four timer thresholds.
code:
Score   2000   4000   7000  10000  14000  20000  28000  35000
-----  -----  -----  -----  -----  -----  -----  -----  -----
Timer     20     60     60     80     A0     C0     E0    100
          18     30     50     70     90     B0     D0     F0
          10     28     48     68     88     A8     C8     E8
           8     20     40     60     80     A0     C0     E0
for every threshold that is above your current timer value, the "rank" increases by 1.

this means that, when you enter an area, you'll get the default set of enemies, until you score at least 2000 points. then, the longer you're in the stage, the lower your 'rank' gets. higher ranks tend to have harder enemies. this seems a little weird, huh?

anyway, now that we have your rank, it's time for the third part, your health:
  • check the health of object slot 0.
  • is it below half? (64 / $40, four blocks)
  • if so, decrease the rank by one.
  • is it below a quarter? (32 / $20, two blocks)
  • if so, decrease it by one again.

there's just one tiny, insignificant detail about this: your health isn't stored in object 0. it's stored elsewhere. :doh: what ends up happening is that the "is your health below x" checks always pass, so your rank is always decreased by two points.

this means that, effectively, your rank is always 0, 1, or 2. it is impossible to experience rank 3 or 4. :thumbsup:



even if you were able to reach rank 4, the code ANDs the rank with 3; 4 & 3 = 0. so even if it rank calculation did work properly, it wouldn't work properly. :thumbsup:


in practice, it doesn't really matter. the rank system doesn't seem to have ever really been fleshed out. most areas have the same enemy set for every rank. here is an attempt at a table; the initial enemy set is the first value in the table, i think.

code:
rank  0 1 2 3
----  -------
1-00: 0 0 0 0
1-01: 0 0 0 0  (* duplicate of 1-00's data)
1-02: 0 1 2 2 
1-03: 1 1 2 2
1-04: 1 1 1 1
1-05: 4 4 4 4

2-00: 0 0 1 2  (* this is the only one to contain a unique unreachable value)
2-01: ???????  (* appears to point to a pointer, not an enemy set. used for the dam)

all 3-XX entries point to the same values.
3-00: 2 2 3 3
    .....
3-06: 2 2 3 3

all 4-XX entries point to the same values.
4-00: 4 4 4 4
    .....
4-17: 4 4 4 4

5-00: 5 5 5 5
5-01: 5 5 5 5  (* 5-01 points to 5-00's data)
5-02: 6 6 6 6
    .....      (* 5-02~5-08 all point to 5-02's data)
5-08: 6 6 6 6

6-00: 4 4 7 7
6-01: 4 7 7 7
6-02: 7 7 7 7
6-03: 7 7 7 7  (* 6-03 points to 6-02)
as you can see, most areas just have the same enemy for every slot.


summary:
  • the game keeps track of points scored in a particular area.
  • the game tracks how long you have been in that area.
  • based on the above, it calculates a rank. higher scores = longer timer thresholds.
  • the higher the timer is, the lower your rank. the higher your score, the longer it takes for your rank to go down.
  • you can increase your rank by crossing a score threshold that increases the timer thresholds above your current timer.
  • the rank code always considers you to have 0 health, so it lowers your rank by 2.
  • even if it didn't, a rank of 4 (highest) is considered a rank of 0 (lowest).
  • even if it worked properly, the rank system is unused in most of the game.

:bravo:

i don't think anyone knew about this system before. probably not even konami, honestly.


if you want to experience the rank system without the health factor, you can use game genie codes GKSAGOSX + IXSAIPAT + XESATPIA. it doesn't change a whole lot.



addendum: here are the sprite zero wait loops where the game spends most of its time. still a lot of work to do, like finding out where and when it renders the status bar (not a cheap thing to do...)





it was like 4 am when i started writing this and now it's 6 am, oops. and i forgot the anecdote i was going to post after this too. shit i remembered, the blue marlin! you aren't getting away that easily

Zamujasa has a new favorite as of 05:52 on Jun 13, 2022

Zamujasa
Oct 26, 2010



Bread Liar
:chome: BONUS POST :chome:


THE BLUE MARLIN

a weird fishing game, i guess? i helped some speedrunners by finding the exact ranges for the possible winning weights on every day, but i also found out that this game has a really nasty bug.

you see the weather here? cloudy?



it doesn't matter. the game always resets the weather to "fine" after this screen.



like the comment says, game genie codes TVXOKXSE+OXXOSZNO will fix this bug. the most notable effect is that, on cloudy days, the animated sun that hangs out in the corners of the screen isn't shown.

i actually ran into someone streaming speedruns of this game on twitch a month or three ago and got to talking about this bug, and they were pretty stoked that there was a fix. supposedly the weather makes a difference in what lures/etc. work best, though i wouldn't know anything about that. it seems like most of the fishing options are dubious at best and actively detrimental most of the time (you can give yourself 500 feet of line instead of 100, but why would you do this)



bonus bug: the music that plays when a marlin is on the line is missing its noise (percussion) track. you can hear it if you play it in the sound test after playing a different song first. good job team.

bonus fact: this game has its own scripting/bytecode system that it uses to handle events. ca4hafhefe/sanchez has an IDA script that decodes it, though i haven't tried getting it to work / bothering with it.

speedrunner tips: the winning weights for all four days of the tournaments are randomly chosen from within the following ranges:
day 1: 300 ~ 427
day 2: 480 ~ 607
day 3: 600 ~ 727
day 4: 700 ~ 827

if you read these and go "that just looks like number + rand(0, 127)", well...

Zamujasa has a new favorite as of 05:27 on Jun 13, 2022

Zamujasa
Oct 26, 2010



Bread Liar
i got bored and a friend suggested battle of olympus. a friend speedran this game for a while and i'm sure she can tell you she's heard olive the jokes about it. :dadjoke:

anyway as usual i ran a tas through it with the code/data logger on. unfortunately, the japanese game came out earlier, and the tas is for the us version. i like to do the earlier versions of games because they're more likely to have interesting stuff left over

one of the first things i do after i start a disassembly in ida is just hiding the huge regions of the nes's memory map that aren't used:

- $0800~$1FFF: mirrors of main memory, $0000~$07FF
- $2008~$3FFF: mirrors of the ppu registers, $2000~$2007
- $4018~$4FFF: mirrors / other unused crap from the audio system, gamepad, and expansion ports
- $6000~$7FFF: optional program rom or ram a cart could provide, for carts that don't have either

(for that last one: mario 1 does not use this space; mario 2 and 3 use it when decoding level layouts; the legend of zelda uses it for your save files)

normally this is a pretty banal procedure and sometimes it reveals instances where the disassembler mistakenly thought some data was referencing one of these. it's uncommon ever since i started every disassembly by immediately undoing the auto-analysis (which frequently seemed to randomly decide what was code and data).

not this game! :aaa:



this is an actual, real referenced memory address in code!



so, what's going on here? why is it doing this? there's nothing there, it's just reading open bus, which is basically line noise. it also doesn't look like the flags a BIT instruction modifies are used, since the INX changes N and Z; only V is left over. and again, this is junk data.

well, the answer is that this is kind of a bait-and-switch, on my part. :v: what happens is in the eye of the beholder, or in this case the program counter.

if we back up a little in the code:



see the branch (BEQ loc_61E9B+1)? the "+1" is important here -- it means that it's not jumping to that location, it's jumping to the next byte at that location, because it's already part of a decoded opcode, and ida has no clue how to represent two opcodes sharing the same space. well, that's why we're here -- we can figure out the logic behind this and add notes to understand what's going on. (and make the disassembly a little clearer, i guess.)


to explain it in something approaching layman's terms, it's like starting to read a word a few letters in.

code:
   0 1  2  3 4
  that my side
    at my side
if you start from the beginning, it's awkward sounding, but it's valid.
if you start from two characters (one byte-ish, in this example), you get a different sentence with a different meaning.

that's exactly what's happening here. the first opcode is an awkward construction that acts like a no-op; skipping into it changes its meaning without needing to take up more space with more branches.

code:
  2C A9 7F   BIT $7FA9   ; clobber N Z V, but doesn't matter
     A9 7F   LDA #$7F    ; load A with #$7F
this is what it looks like with some rudimentary labels and comments:




this isn't really rare, though. it's more of pretty uncommon. i believe that super mario bros. 1 also does something like this as well to save a byte or two but i don't remember where



FUN FACT: super mario bros. 1 has something like 13 bytes free. this is only counting outright unused bytes, and not space that could be freed up by removing dead code or optimizing crap.

Zamujasa has a new favorite as of 23:07 on Jun 14, 2022

Zamujasa
Oct 26, 2010



Bread Liar
this isn't much of a weird thing nor is it NES related, but it is close; mostly a fun story. i used to frequently corrupt games, often live on twitch or the like, by using a lua script that randomly wrote crap to the game's memory when i pushed a button. one of those games, link's awakening, at one point exhibited strange behavior after some corruptions: Link moved around the screen faster than normal and ignored all collision.

the corruption script doesn't save any kind of history of what it does, because it writes garbage everywhere, and trying to pare down which particular corruption (or sequence thereof) did something would be a huge pain in the ass. but i did think that was suspicious, so i took a savestate for later.

i wanted to find out what caused this, so i used vba-rr's lua scripting again. i loaded my savestate and moved link to the right edge of this screen, in the out-of-bounds area:


the script would then
  • load that savestate
  • write a 00 to a memory address
  • hold left for ~120 frames
  • check if link's x position was near the left side of the screen
  • if it wasn't, print a line to the console
  • do this again but with the next memory address

this gave a few results; some were false alarms; one set link's x position, so holding left caused a screen transition, a few crashed, etc. but the list was fairly short and i manually tested them out one by one after it was over. the culprit was $C17B; setting it to a nonzero value enabled walk-through-walls mode.



some time later, someone with actual game boy disassembly experience figured out that all of this was managed by two or three bytes at the start of the ROM, along with a host of other fun things.



(much earlier in life, when I was a young teenager, i managed to find a game genie code that would cause game & watch gallery 3 to jump to its debug menu, though i never could remember which one it was. thankfully, someone else managed to find it once i told them it did exist.

it's still the only way to change the tempo of the music room music, pah.







e: reminds me that the "Disabled Redraw" section of link's awakening's tcrf article has some hidden history that was explained by the gigaleak source code; it doesn't "disable redraw", that flag is actually causing it to draw a particular tilemap to the subscreen. the problem is that it was supposed to be the blank one, but the source commented out the 'blank' tilemap and replaced it with a 'test' tilemap that, well. you see what it does.

the article is also missing the fact that there's still remnants of when the game would let you have multiple medicines and how it showed them on the subscreen; when you die and a medicine is used, it actually writes the number remaining to a particular place on the subscreen, but since the subscreen isn't actually shown at that point + it redraws itself when you open it, well. fart.

Zamujasa has a new favorite as of 08:38 on Jun 17, 2022

Zamujasa
Oct 26, 2010



Bread Liar
been a few days of not doing any disassembly because my real life job has made me want to curl up and die. :capitalism:


anyway, on to the wing of madoola, an early(-ish) game by sunsoft. god damn sunsoft made a lot of nes games, huh


there's a prototype out there that's mostly the same as the final, but with a few curious changes. this one's not even on that one russian guy's site (sorry, cah4e3, i'll remember it some day), and tcrf doesn't even have an article on this game at all. shameful!

one of the differences is that the prototype has code where the final game only has NOPs, a sign that they commented out something:



the prototype has a check if you're holding select when you start a game. if you are...



...the top half of this code is run, which gives you 9990 Hits, 9990 Magics, and every item level in the game (there's some additional code below this block). it also sets your highest-world-reached to the maximum, so you can use the continue menu to go to any stage.




later on in the game proper is a secret you can uncover, which there is apparently no easy-to-find screenshot of online. weird. anyway, it's a cat face (i think) and says "THE KEYWORD IS" and then some joke based on atlantis no nazo's gorowase, where the message "KEY WORD NAGOYA" was printed above a pyramid and you had to throw bombs on moai hids a certain number of times to warp to an otherwise unreachable section of the final stage that gives you 2000000 million points (or 4M, if you have the double score powerup).



my comments point out that the way of loading a value here, LDA rom_offset, don't really make sense. it's not using an indexed read, and it's reading from ROM, so whatever value it's reading is never going to change; they could have just used LDA #value_at_that_address and saved a byte (or two).


EDIT: right, the only other(?) major change the prototype makes is that the secret text doesn't appear


this post is probably a bit more rambly and incoherent than normal because work's been interfering with my sleep, and i haven't really looked at the wing of madoola in a long time. i just remembered something stupid and, well, that's what this thread is for.

Zamujasa
Oct 26, 2010



Bread Liar
didn't sleep before work, oops. my roommate has suggested starting a patreon where i make posts like this (but probably formatted slightly better).


anyway, here's an oddity:



there's a bunch of other weird junk in this "game" that i've found, but this one is something we might never understand. see, $42FF in the nes memory map is... nothing. there's nothing there. in theory, the cart could have put something there, but in this case it appears to be nothing. this code also isn't called anywhere, and is otherwise sitting out near the end of the ROM, surrounded by empty space.

E: to be specific, this is what it does:
code:
write #$C0 to $42FF

wait:
  load value at $8888
  if value != #$85		; note: should never happen??? this ROM does not have bankswitching capabilities
    goto wait

write #$A0 to $42FF

reset
the hardest part about disassembling this is that the only ROMs I can find are ones that've had their title screens blanked. :cripes:

this is dian shi ma li, also known as "big tv mary bar" or "PUSH START TO RICH!". i'm pretty sure this was intended to be an actual gambling game and not just a janky as fuck "game".

Zamujasa has a new favorite as of 07:30 on Jun 22, 2022

Zamujasa
Oct 26, 2010



Bread Liar
a message came in:

XkyRauh posted:

Hey! In the latest post you made in your "dumb shit in roms" thread, https://forums.somethingawful.com/showthread.php?threadid=4004323#post524327386, the cheat code uses a bunch of $99 or $90 to give the player bonus HP/MP. Any idea why they did it that way vs. using $FF, the true highest? The 99 vs. FF thing is something that could use some explanation, if you're willing.

Thanks for making such a fun thread to read!

there are a few reasons. to really explain it, i'll have to explain a few other things first.


hexadecimal numbers and you, or: "Zero to F this"

for the most part, computers handle numbers in hexadecimal formats: base 16, rather than base 10 like we use. there's a lot of technical bullshit behind this that i don't care about, something something powers of two, etc, whatever. but for the most part, your number system when dealing with low level computer stuff goes 0123456789ABCDEF.

the nes used an 8-bit cpu. every bit gives you an additional power of two, so 28 = 256, or 00~FF.


generally speaking, there are a few ways to represent normal human numbers, and they all have their own various plusses and minuses (sometimes literally :haw:)


decimal mode
the 6502 supports "decimal mode", which makes the processor do math in "binary coded decimal (BCD). this effectively means that the value of a byte is what it's read as if you look at it as if it was decimal: #$11 is actually eleven, not seventeen as it would be in hex. this i controlled by the D flag.

the NES does not use a 6502. it uses a generic brand 2A03, which was a licensed clone of the 6502. to save money, the 2A03 does not include decimal mode, and every NES game was generally required to ensure it was disabled as part of its startup routine. oh well.


converting from hex to decimal
some games store an actual value in hexadecimal, and then convert it to base 10 for display later on. the legend of zelda does this; your rupees are directly stored as a value from 00~FF, and they're rendered in base 10 on the status bar. this is also why you have a 255 limit. there's technically nothing that stops you from doing this with any arbitrarily large values, it's just more math.

some games put in a token effort and then give up. super mario bros. 1 famously uses a crown as the tens digit... and only a crown, letting the ones digit continue to rise into the alphabet and beyond. super mario bros. 2 tries a little harder by actually calculating a tens digit, but then you start having A0 lives at 100 all the way up to P5 lives at 255. super mario bros. 3 just capped it at 99.


one byte per digit
this is great if you're only ever doing additions and subtractions and don't care about memory usage. basically, every potential digit of your value is a single byte, usually in order from least to most significant, e.g.

CREDITS = 123456

$0400 >> 06 05 04 03 02 01


this is incredibly easy to display, since every tile is one byte, and for math you just add or subtract each digit, just like on paper, doing carries as needed. you can even do the math in a loop, iterating over every digit, letting carries/borrows propagate as needed. division and multiplication are a bit slower and you use a lot more memory, though


we'll use our own decimal mode, with blackjack, and hookers
mostly this is just the game, indeed, reimplementing decimal mode. shift right four times (basically floor(value / 0x10)), there's your second digit. slightly more complicated math, but if you're not constantly fucking with the value, it's probably not a big deal. display is done two digits at a time. some games used this; i want to say the legend of zelda: link's awakening uses this method, where a max rupee count is 99 09. but i could be wrong, i didn't look into it too closely.




okay, but we're talking wing of madoola now: why 9990 and not, say, FFFF (65535)?

because wing of madoola doesn't have a way of displaying more than four digits, or anything other than 0~9. the health and magic point displays are always four digits.



there's no A~F in the tileset, and using those numbers causes visible tile glitches above. that said, nothing really stopped them from using native hexadecimal values and converting them for display. they just didn't.

frustratingly, my disassembly doesn't seem to have any obvious locations where damage or healing is done, so i can't check how this game in particular does its math (and i probably wouldn't understand it right away, anyway).

Zamujasa
Oct 26, 2010



Bread Liar
jackpot vs dian shi ma li:



clearly based off of the same code. it looks like dian shi ma li is a very hacked up version of jackpot with a lot of things stripped out. i've been cross-referencing one while working on the other and while many things are different, a lot of things are the same, or act in similar ways. mostly i just need to keep them both open so i can remember the names i use, since i don't have any hard and fast naming conventions.



this is more of a preview version of the kind of posts i'd do here (or increasingly, elsewhere; starting to consider setting up a blog and patreon and using this as my first drafts :v:)



first, some notes i've been keeping on dian shi ma li. i intend to make a lua script based gui for the game. maybe if i want to get real fancy i can even make it so that it hijacks joypad input entirely


code:
-- cherry            1P A
-- apple             1P B
-- orange            1P Sel
-- lime              1P Start
-- bell              1P Up
-- melon             1P Down
-- star              1P Left
-- seven             1P Right
-- bar               2P A
-- start early       2P B

-- 06F8: dip sw1
-- 06FC: dip sw2
-- dip sw3 is p2 input (????)

-- 1p/2p
-- up: go to double up
-- down: take winnings
-- in double up
-- 1P left: big  right: small
-- 2P sel: big  start: small
i found what appears to be the tables for the ... what do you even call it? roulette?

quote:

:bong: i'm stoned, interrupting thought time :bong:

a common misconception is that the super mario bros. 3 bonus game is a "roulette". that's wrong! it has nothing to do with roulette whatsoever. it's actually a slot machine; consider the spinning reels to have individual symbols like three mushrooms, flowers, stars, and you'll get the idea. i don't know why, but this misconception bugs me a lot. maybe it has to do with growing up in vegas.

anyway, i listed out every 'space' the roulette could land on, and how many options it had for hitting that space, and basically came up with the first stage of odds (there are other things that affect payout)

anecdote posted:

most classic reel slot machines have two faces; the one they show you, and the internal one. generally, multiple internal 'stops' correspond to one external 'stop'. this is how they work the odds.

for example, a classic slot reel would look something like "blank, cherry, blank, bar, blank, seven". internally, there would be multiple entries for certain stops, making the reel more likely to land on specific spots.

physical reel: ◻️🍒◻️📊◻️🎰
internal reel: ◻️◻️◻️◻️◻️◻️🍒🍒🍒🍒🍒◻️📊📊◻️◻️◻️◻️◻️◻️🎰

you can see how changing the frequency of things that appear means they're more or less likely to be hit. naturally spaces next to the highest paying symbols show up more often to give you that "close miss" feeling.

here's a picture of the game board and the internal stats for it. again, this assumes the game is fair. it's not kind of the double up game absolutely is rigged



i don't know why fortran has red teeth. i don't think i want to know.



unsurprisingly, the anecdote is relevant here: while it isn't much, the three spaces nearest the highest paying symbol occur slightly more frequently than the others.

wait, a BAR payout is 50, and this has a probability of over slightly under 4%. doesn't that create +EV?
- i'm not sure how to best explain the math here, but the payout is 50:1 (that is, fifty to one). the odds of landing on that square are about 1:25 (one in twenty five); if you multiply these together, you get ~50:25 = about 2:1 odds.
- this is what we in the biz call a "player advantage".
- in reality, i don't know how fair this part of the game is yet. i have my suspicions, which i have to talk about more, huh. shit. i have a lot of writing to do about this
- but it does basically mean that, yes, betting on the bar will (on average) cause you to accumulate credits.


anyway, the payouts are further complicated by two mechanics:

- "ONCE AGAIN" space seems to just wipe your low-three bets (bell, lime, and orange).
- "COIN" space lets you "PUSH START TO RICH!", earning 10 credits per press for a limited time. a friend of mine, acmlm, suggests that the best possible result with TAS is 1080. i'm not sure what determines the length of the timer other than a guess that it's your bet.
- the two groups of three (7-star-melon and bell-lime-orange) use a secondary roulette to choose the payout, between 40-30-20x or 20-15-10x. i'm not sure what determines this yet.
- fortran jumps up and hits a box after getting a hit. the box can reveal the same symbol (doubling the pay) or a gross moldy mushroom (tripling it). i'm not sure what the odds are of this either.



god this post kinda went everywhere. 'm sorry it's an adhd thing. i'm working on a second post about my thoughts on how this game works



E: i think my odds calculation above omitted the non-paying spaces for once again and coin, oops

E: i did. fixed. the updated image also has a section for the probability/returns (mostly unmarked since it's not accurate).
the odds confused me because a. i'm stoned and exhausted and b. unlike many games where you bet and win a payout based on the result like poker or slot machines, this one has multiple bets you can make, each with their own odds. it's more like big six.

the winning strategy continues to look like "just bet max on bar", but again, the game probably (definitely) cheats.

Zamujasa has a new favorite as of 01:47 on Jun 23, 2022

Zamujasa
Oct 26, 2010



Bread Liar
one obvious advantage of using this as a dumping ground for thoughts and notes is that i can get everything out of my system and then figure out how to organize it later. anyway, on to a slightly different subject:


why does this stupid buggy pirate gambling machine interest you so god damn much

the first and obvious answer is that this game is full of weird secrets and decisions, and i am almost certainly one of the only people to have walked through its halls and tried to decipher its secrets. i think that's pretty cool. it's not every day you get to do something nobody else has done.


the second and truer reason is that this game is very clearly a type of "secret gambling game". looks like a harmless for amusement only video game, actually intended to be used to gamble. the pinball hall of fame has a target roll machine laying around, which is a sort of physical version; the spots on the wheel the ball can go into are the same distribution as the rolls on a pair of dice, with obvious craps use. illicit gambling, in this establishment? it's more likely than you think!


the tcrf article has screenshots of unused diagnostic screens like you'd see in stand-alone games:



what the article does not mention is that these screens actually do function.

the left screen shows the number of times every type of square has been landed on.
the right screen shows the current dip switch flags, and updates them if they change.

both screens:
- counts the amount of coins/bills added
- shows the total number of games (GAMES), the current amount of credits (CREDITS), and the total amount of credits bought (TOTAL)
- shows the configured value for coins and coupons (bills)


the dip switch settings ordinarily can't be changed. "SW3" is the same as the P2 controller, but is otherwise not really used. "SW1" mostly controls the money value. i haven't really decoded what most of the switches do, though at first glance it looks like they were intended to influence how the game played, possibly by skewing the odds. i'm not sure and it looks like the code was cut off.


here's what i have for DipSw1 so far:



code:
dip sw 1
7    Does nothing; may have enabled checking DipSw2
----
6    Coupon (bill) value:   ON  120   OFF 100
----
5    Unknown; the value of these two flags is
4 *  stored at $4AD but I'm not sure what that does
----
3 *  Unknown; sets to values to   * ON  20/180   OFF 10/90
----
2 *  Coin value:      ON / ON   50    OFF / ON   10
1 *                 * ON / OFF  25    OFF / OFF   5
----
0    Force reset
Switch 7 literally does nothing:




The value of coins and coupons bills does work. Under normal circumstances you can't add coins, but the game does simulate bills being added when you press start on the title screen. In something of an awkward way, at least.

Basically, it just sets there as being a pending bill inserted and then runs the routine that processes it, ten times in a row.



The game also considers you as having inserted a bill if you press 2P start while the roulette is going. every press will be another 100 credits.




the real mystery, for me, is how much of this game actually exists. was this ever actually finished for use as a gambling game? was this a prototype of that idea that they quickly reworked into being a normal video game? who knows.


i'm starting to fade a bit (always great right before work :v:), but here's some evidence from jackpot of their intentions...



"for amousement only" indeed.

Zamujasa has a new favorite as of 07:09 on Jun 23, 2022

Zamujasa
Oct 26, 2010



Bread Liar
been pretty under the weather between my grandfather kicking the bucket a week ago (:rip:) and also catching covid. been a fun week!

:coronatoot:


spent a bit of time poking dian shi ma li again because i enjoy suffering.

first, an oddity:


there's a lot of code in this 'game' that appears to be either left over, stripped out, or never added. this one, for example, loads a value into A, then returns. and (in this case) ... immediately overwrites that value with something else, rendering the entire subroutine call completely pointless. cool.

if you nop out the relevant code so that the code drops into the apparently unreferenced code below it, the only change i've noticed is that the credit display will print out "00" after the number when initially adding credits.



i have no clue what the point of this is, or if it's correct (it probably isn't).



second: i think i found where the game handles the "grouped" symbols. those are the ones with the payouts in blue:


basically, if you get one of the symbols in the group, the payout is randomly* selected from the options above the set, e.g. if you get a seven, star, or melon, the pay will randomly be chosen between 40, 30, or 20 credits.

to the surprise of nobody, they are not evenly distributed:



basically, the top payout has a 1:8 chance, the second one has a 2:8 chance, and the lowest payout has a 5:8 chance. i updated my little spreadsheet with the average and, surprisingly, things seem to be evening out:



what i'm not sure about is the bar payout. it's flat twice the rate of all of the other things. it could be fixed by having only one 'slot' for it (it currently has two), but i've double checked and it's definitely in the list twice. i get the feeling there's still some other bullshit going on behind the scenes.

the high/low game is absolutely rigged, though i haven't found the code for that yet.

Zamujasa
Oct 26, 2010



Bread Liar
occasionally cross-disassembling between jackpot and dian shi ma li continues to provide insight; in this case, i found the strings table that the game uses to write certain messages to the screen. there's a lot of outright gibberish that doesn't make sense in the latter, and lo and behold it does appear different in the original jackpot game:



they clobbered several entries and expanded the table a good bit, but i'm not sure why since they didn't use the additional entries. i haven't tried to figure out if the leftover data is meaningful because it's definitely at least somewhat different from jackpot, but at the same time it's still gibberish

if you're wondering why the unknown numbering starts at 11 and then goes to 1 later, it's because i started naming them at 1 and realized a bit later "oh, right, there's a few before here too" but didn't want to go fix all of them :v:

Zamujasa
Oct 26, 2010



Bread Liar
this game's "double up" is a card game, where you pick BIG or SMALL, and you win if the card drawn is, well, BIG or SMALL.



it also presents you with a history of the last few cards drawn. at first glance, it seems sensical enough: you choose, it draws one of 52 cards, you win or lose.


the cards are completely irrelevant

Zamujasa
Oct 26, 2010



Bread Liar
Starting to fade because it's 5 in the morning and I have a MinuteClinic (TM) appointment for covid treatment in a few hours. I should probably be in bed, but instead I think I figured out how the hi-lo game works.


  • First, check if the cheat flag is non-zero.
    • If it is, decrement it.1
    • If it is non-zero, you will win the hi-lo game.
    • If it is zero, you will lose the hi-lo game.
  • Check if the hi-lo game should use alternate rules.
    • Add your current credits + your current winnings � 2. Call this your "potential total".
    • If this "potential total" is less than or equal to a "inserted credit tracker"2, do not use an alternate threshold; continue below.
    • If it is greater, compare your current credits to your current winnings.
      • If your current winnings are greater than your current credits, you will lose the hi-lo game.
      • If your current winnings are less than or equal to your current credits, use a threshold of 28.
  • Otherwise, do some things I don't understand yet to choose a threshold from {38, 42, 42, 46}.
  • Randomly pick a number from 00 to 99.
  • If the number is less than the threshold, you win the hi-lo game.

The cards are just for show and don't matter.


tl;dr version
Stop after the first condition that matches:
  • If (credits + winnings * 2) < 10003, you have about a 4-in-10 chance of winning.
  • If (winnings < credits), you have a 1-in-4 chance of winning.
  • If (winnings > credits), you have a 0 in ∞ chance of winning.


Basically, don't play the hi-lo game.



1 Technically it checks against the pre-decremented value, so you win if it was >= 2 and lose if it was 1 (so the normal game happens if it was 0), but for the sake of this description it doesn't matter

2 "credit tracker": This goes up when starting the game / "inserting" "money". The first 400 credits are added to this counter at 60%; after that, it jumps up to 80% of your inserted credits. Given that the game always inserts 10 bills = 1000 credits, this counter will always typically start at 800. Under some circumstances it also increases during gameplay but I don't know what causes it.

3 "1000" here is the credit tracker above. This is just an abstraction because I have no idea how this counter works and it tends to park itself around 800~960 and stay there. If you mash Start to add additional credits, this becomes less accurate (but, generally speaking, you still shouldn't play this stupid game)

Zamujasa has a new favorite as of 04:51 on Jun 30, 2022

Zamujasa
Oct 26, 2010



Bread Liar
covid sucks, avoid it like the plague. i managed two years covid-free and boy am i glad it was long enough for them to come up with paxlovid. i still feel like ass but at least i'm not dead :toot:


recently we were told a good dump of the bugs bunny birthday blowout prototype was free to be released (just, nobody had yet). so it got posted a few days ago and me and my roommate have been looking into it.

the old dump exhibited damage consistent with someone putting the eproms in backwards and frying them a bit, iirc, the new dump doesn't have those problems and finally cleans up a lot of the corruption throughout, most notably making stage 4 actually look reasonable. real cool


anyway, she has so far found that pretty much every object in the game is already programmed in, though some of them have buggy behavior. they can be placed into levels and work normally, though so far i think none of them except an elmer are hanging out in-game

i've been disassembling it and recently found the pointer tables to several pieces of level info, namely the enemy spawner tables, the warp tables, and the overlay tile tables (carrots, breaking rocks etc). here's a few hours of work smashing some stuff together in php, showing all of this data in some sense:



this is level 1-2; the white area is the initial camera zone, the B is bugs's spawn position; bold gray numbers are enemy IDs, dark red numbers are the tiles (7E is a carrot, for example). green boxes are warps and camera lock zones respectively

the main thing that's missing is the level layout itself, either with collision visible or with actual tiles. (actual tilemaps for enemies might be good too but I'm not sure how feasible that is without a lot more effort)




i've also looked into some other things:

  • the prototype has a mysterious "wavy line with bugs head" powerup. it sets flag #$04 but otherwise does nothing; the game never checks for it. the final game replaced it with a health restore pickup, but it still sets powerup flag #$04 (and nothing reads it)
  • the hammer is powerup flag #$02 and nothing explains what it does ... namely it makes things die in one hit. most things in the prototype take multiple hits; in the final game it's mostly just the bosses, normal enemies typically die in one hit. but going to the boss room cancels the hammer powerup, so they're mostly worthless...
  • the mole game worked quite differently and the early version here has a few remnants of what it was originally designed to be

the mole game in the final version became a timed section where you had to hit as many as you could in a time limit, getting more 1ups the more you hit. when i was poking this game years ago i already found the table for how the requirements, but for the most part there wasn't much of interest, other than the "SOUND MODE" text was in that bonus game's text

in the prototype, and from leftover graphics in the file, the mole game was supposed to involve getting a prize from hitting a mole; 'SORRY!", "1UP", "2UP", "3UP". the prototype game doesn't actually function beyond popping moles up, letting you move and attack, but not actually checking for hits or doing anything.

the code to handle spawning the moles changed completely between this and the final as best i can tell, and the prototype actually has a few references to the lives bonuses. when the game is spawning a mole, it actually runs some code to determine what "type" of mole it should spawn:



nothing seems to actually check these values, but given how the code operates, it's a fair assumption that this was involved in the lives gimmick.



e: the one fucking time i don't use preview i typo the list end

Zamujasa
Oct 26, 2010



Bread Liar
here's how i would rewrite this particular subroutine:

code:
MoleGameChooseAndSetMole:
    JSR MaybeThrobRNG       ; Get new random byte in A
    AND #$3F                ; mask off higher bits; 1/32
    BNE _Next1              ; If non-zero, check next
    LDA #3                  ; store mole 3
    BNE _StoreMole

_Next1:
    AND #$1F                ; mask off next bit; 1/16
    BNE _Next2              ; if non-zero, check next
    LDA #2                  ; store mole 2
    BNE _StoreMole

_Next2:
    AND #$F                 ; mask off next bit; 1/8
    BNE _Next3              ; if non-zero, skip ahead
    LDA #1                  ; otherwise, store mole 1
    BNE _StoreMole          ; and skip load 0 below

_Next3:
    LDA #0                  ; store mole type 0
_StoreMole:
    STA MoleGameActiveMoleFlags,X    ; store mole type
    RTS
the optimization here doesn't matter; the mole game is one of the very few places this game runs at a full framerate (it is very poorly optimized; kemco was not a vry skilled nes developer).

but you can kind of get an idea of what the problem with this game's code is. it doesn't use branching instructions well, it doesn't optimize memory or register use, it's just very strangely written in general. the running guess is just that their programmers were far more familiar with a different architecture and didn't "get" conventions here, but at the same time their z80 output on the game boy wasn't exactly stellar either (if anything, arguably worse)



e: i should also mention that this inverts the routine from checking for all bits set to checking if no bits are set. the result is the same (assuming the rng isn't fucked, but if it is all bets are off anyway)

as for how the math works, it removes a bit from the rng to see if all bits are zero. checking if all bits are 1 means you have to do a CMP against the expected value; checking if they're all zero means you can just use the zero flag. :v:
code:
RNG   11010000
1st   __010000 -> fail
2nd   ___10000 -> fail
3rd   ____0000 -> PASS

Zamujasa has a new favorite as of 23:33 on Jul 5, 2022

Zamujasa
Oct 26, 2010



Bread Liar


:thumbsup:


Game Genie code ATTYKA will disable the second call, which results in the game playing the music at inconsistent speeds. It's awful.

Zamujasa
Oct 26, 2010



Bread Liar
Ended up working way too much on this stupid thing and now it displays most of a level, though in a incomprehensible format littered with my programmer art. Here's the level map for, uh. 1-4?



It'd be fun to do a writeup of how these levels are put together, because it's convoluted and stupid. Short version:

  • Every stage (world) has 128 "chunks" of 4x15 tile layouts
  • Every round (level) is made up of 64 chunks
  • Every round is either long (64x1) or tall (32x2) chunks; that is, $800x$1E0 or $1000x$F0 pixels
  • On top of the chunks are directly-placed tiles, often used for carrots, platforms, or the like; these are drawn directly over the layout after the chunks are loaded
  • Then there is warp data, which specifies a starting position, ending position, and camera bounds (not fully understood)
  • After that are enemy spawn tables, with 60 slots per stage; most stages tend to only use the slots near the end
  • There's also music data and CHR-ROM banks to use (per-stage)
  • Also per-round initial camera ranges and starting Y position (starting X position is fixed)
  • Every stage also has metatile definitions that dictate what 8x8 tiles make up a 16x16 tile, as well as its palette, and its collision type

Stages 5 and 6 are missing metatile definitions and most other data, but a few stages have leftover data. There's one place where there table just does not have entries for all 6 worlds and ends after the initial 4.

Stage 5-1 has a few carrots placed, and Stage 5-2 has some warp data left over that seems to match what would end up there in the final. There are a few places where level data has been shifted around but they hadn't gotten around to updating the warps yet, or one very obvious place in 4-2 where they just blocked off the level with a vertical line of tiles.


It'd be fun to make more of a visual demonstration of all of this, but so much effort. Honestly maybe I should just make a Google Slides presentation and try to do a voice-over or something, that's basically how I made the SMB3 wild card video (without the voice). :v:

Zamujasa
Oct 26, 2010



Bread Liar
haven't been feeling up to it lately for various reasons, but wanted to mention a few places you can read more stuff in the same vein as this thread:


the southbird 2011 smb3 disassembly

this thing is loaded with information. the disassembly has a few more interesting things in it, but this page covers most of it in good detail. i think the wild card is one thing that isn't mentioned, though. super mario bros. 3 was one of those games that just has a wild amount of shit left over in it, and we'll probably never know what most of it was about.



sonic 3 unlocked - here's the first post (use "newer post" at the bottom to go through it)

really cool blog that goes over a ton of the inner workings of sonic 3 (and knuckles), including how some of the programming tricks worked, why certain things act the way they do, etc. maybe i'm weird, but i tend to re-read this entire thing once every year or two.

plus, it has one of my favorite silly phrases to use: "foreshadowing: the sign of a quality blog." (or whatever). :v:




fun side-rant:

it seems almost stereotypical, but the super mario world and megadrive/genesis sonic communities are really different in how they go about things. maybe some of this has changed in the last few years, but generally speaking, the sonic community was pretty focused on working on a group disassembly, basing most of their tools/work off of that. that allowed them to share and improve a lot of changes throughout the game and build upon it, which is why there were a lot of "big" hacks with huge gameplay differences. the sonic retro wiki even has a tutorial for porting the spin dash into sonic 1 as an example for new-ish people.

the smw scene is almost entirely bound by Lunar Magic, likely one of the most well-known ROM hacking tools out there. unlike the sonic scene's largely open and collaborative effort, lunar magic is an application by one person, fusoya, who has steadfastly refused to share the source or even most of the inner workings. super mario world by default has a ton of restrictions on mechanics -- where levels can be placed on the overworld, how level changes work, some other stuff -- that is entirely papered over via magic, undocumented assembly hacks applied the instant you open a ROM. in addition, the tool itself has a check where, should you modify the executable at all, it will pointedly refuse to start

it also included a feature that would "lock" your hack so that lunar magic would refuse to open it again. iirc this was first used for, surprise, fusoya's "demo world: the legend continues" hack, which was (at the time) one of the more impressive hacks (and a full-game of new levels, no less) to come out

every change that isn't from lunar magic has to work around it, including using special in-rom "tags" (magic sequences) to say "hey LM please do not step on this part of the ROM". without being able to directly edit the code and rearrange stuff...

that's not to say that the smw hacking scene hasn't done some incredible stuff, but from my perspective, it's all in spite of the state of the community, not because of it.



as far as i know, the best smw disassembly so far is this one by IsoFrieze, who you might know from retro game mechanics explained.... but the main problem with a disassembly of smw is that nobody can use it! there are no tools that work with them that i know of, and of course you can't use lunar magic for it.



shrug. lunar magic is very impressive, and it's one of the most polished tools in rom hacking history, but i honestly feel like it's done the community a huge disservice by basically handcuffing the community's efforts to itself.





fake edit: also i need to put the bugs bunny birthday blowout stuff online somewhere. i've mostly finished my level viewer, or at least how much of it i care to bother with, and gained a pretty good understanding of how the game works.

i think the last thing i needed to add was that i found a table of bytes for if a level should be one screen tall (full-width) or two screens tall (half-width), rather than trying to guess based on the data.

Zamujasa
Oct 26, 2010



Bread Liar
Someone posted a link to this thread in the QCS "Post threads you like" thread. I'm glad people like this thread even if it gives me horrible performance anxiety and has tempted me to return to the 2000s era of "I'm not dead, I swear" website updates. I'm not dead, I swear!

Anyway, a quick lesson in checking what you're doing before diving in blind, because sometimes tools aren't smart. (I am also a tool, and thus not smart.)



Mappers and You, or Fuck, can you just use MMC1/3 like a normal person

One of my all-time favorite* games is Pinball Quest on the NES, a quirky but interesting take on the pinball genre. You have 3 "normal" (but fantastical) pinball tables to choose from, then the game's main feature, RPG Mode, in which... well, it's like story mode, except the main character is a pinball. The king gets fucked up, the Princess is captured, and it's up to you to get her back!

RPG mode features:

- Attack Power, or "AT". You have six "circles" at the bottom of the screen indicating your attack strength, which are split into half-marks; a half-filled circle is worth 1, a full circle is 2. You always do 1 damage at minimum, so the first point is actually kinda worthless. You lose half your AT (rounded up) when you drain off a stage.
- Gold. Money! You get money from destroying a few objects and enemies. It only shows up between stages or when in the menu.
- Inventory! Yep, you get items you can use, that range from "stoppers" (plug one of the outlanes or the center hole) or even new, improved flippers...
- Bosses and enemies! They'll usually roam around the table, and defeating the boss gets you to the next stage.
- Also the occasional (singular) pickup on a late table that helps you out.

First, an intermission, because this is one of my favorite parts of this game:



The :buddy: cursor really makes it.



That's great and all, but what about the subject of this post

The NES is pretty limited by default. I think I went over this in an earlier post, but its ROM space is $8000-FFFF, or about 32 KB. You can't fit all the program into memory; thus, enter mappers, which let the cartridge change what ROM is visible to the NES at any given moment. You know how adventure books tell you to go to a new page? That's kind of how mappers work.

Most NES games that use mappers tend to use one of the official Nintendo-developed mappers, like the MMC1 or MMC3. A lot of the reason for this, as I understand it, was that Nintendo was very strict about cartridges; they were almost manufactured by Nintendo themselves, hence all the "Licensed By Nintendo of America" you see. Publishers weren't allowed to make their own carts, partially from Nintendo's iron-fisted rule, but also as an attempt at stopping the deluge of trash that caused the Video Game Crash of 1983. Remember the 2600 and the amount of garbage that came out for it? Yeah.

Well, overseas, things weren't quite so standard. Lots of companies came up with their own mappers, with their own quirks and features. Sometimes these games would have to be ported to the MMC1/3 for US releases...


Okay, but what does this have to do with Pinball Quest, again?

Well, if you're talking the US version, nothing, actually. Pinball Quest's US release is pretty basic.

quote:

Loading C:\Users\Revya\Documents\Emulation\ROMs\NES\Pinball Quest (USA).nes...

PRG ROM: 8 x 16KiB
CHR ROM: 16 x 8KiB
ROM CRC32: 0x2538d860
ROM MD5: 0x131c36c33edac9248730dd8057e1c87d
Mapper #: 1
Mapper name: MMC1

Mirroring: Horizontal
Battery-backed: No
Trained: No

If you're familiar with my writing style, you might figure out where this is going. The "foreshadowing: the sign of a quality blog" joke doesn't really work here because the answer is literally right below this. Sorry, I just like digressions and odd notes.

But... that's the US release. Most of the time, for disassembly and research purposes, you'll want to look at the Japanese release, simply because coming first, it's more likely to have leftovers in it -- the developer might have to rearrange or remove code to fit in a US translation, or other localization changes. This isn't always the case, as sometimes you'll see a US release with leftover tools or the like in it, but it's a general guideline.


So, naturally, I started my disassembly on the JP version.

I had put it off for a while, because when I start disassembling a game, it's usually just to check on some things, or do a basic once-over to see if anything obvious stands out. Most games are too large for a full disassembly effort by one person with too much going on already, and it's not easy to collaborate on IDA projects, so I just do what I can. That's what I did here; very little, wrapped it up, moved onto something else.

Then I got back into the mood from playing it casually yesterday, opened it up, and ... fuck, another digression?


You cannot focus on anything for shit. Bad writer.
yes, and this is with my adhd meds that i have taken for the first time in a month


Pinball Quest (and at least one other NES pinball game that I know of) seems to have areas in ROM that represent the table's collision layout. These tend to be 1:1-scale representations of the table, with each byte representing the 'shape' of that tile, which is then checked against a 1-bit-per-pixel representation. Shown here is the top half of Pop! Pop!:


To view this yourself: Pinball Quest (Japan), data view @ $8D22, tile view 1BPP @ $1FB45)

It's clearly not correct, but you can see that it's mostly there. I suspect there's a lookup table that turns the more esoteric entries into the correct ones, and that's what I set out to find.


My first step was to open the debugger and set a read breakpoint on $8D12~$8E70, which is about where the very top part of the table is. Sure enough, the debugger pops, and I can cross-reference that in IDA and add some identifiers. Standard stuff.



But here's where I notice a very suspicious problem: there are no XREFs to this in IDA. This is a routine that's called constantly; it's not entirely impossible for it to not appear, as there might be indirect jumps or tables that aren't formatted right, but it's very strange. I take the next step of following the execution to the RTS, which will tell me where it's being called:



Okay, simple enough. But IDA clearly sees this jump to $ECEC. Why is it not showing this as a jump to the right routine? Let's follow IDA and see -- oh. oh no.



If you remember the first image, FCEUX is saying 07:ECEC, or bank 7. IDA was showing BANK3:ECEC, which... oh. We're not jumping there because IDA thinks this address is what's currently at $ECEC. Shit.


Remember when I said Pinball Quest used MMC1 in the US? MMC1 gives you a switchable 16KB bank at 8000-BFFF, and a fixed bank at C000-FFFF. That explains why FCEUX is saying we're in bank 7; there are 8 16K banks, and the last bank is fixed at the end of the address space. So what's going on? Why is IDA getting it wrong?

Well, it turns out that the Japanese version of the game does not use the MMC1 mapper. Surprise.

quote:

Loading C:\Users\Revya\Documents\Emulation\ROMs\NES\Pinball Quest (Japan).nes...

PRG ROM: 8 x 16KiB
CHR ROM: 16 x 8KiB
ROM CRC32: 0x475cdbfe
ROM MD5: 0x066127313b2cfdc761d19eb496d8a9b2
Mapper #: 72
Mapper name: JALECO JF-17

Mirroring: Horizontal
Battery-backed: No
Trained: No

The IDA loader I have doesn't recognize this mapper, and defaulted to thinking that this game is actually 4 x 32 KB. Whoops.

Everything I've done so far that might cross a bank line -- i.e., most of it -- is pretty much void. I'm not sure how to fix this easily; I might be able to create a new disassembly, manually edit the segments in IDA, and then run the CDL over it again. That might fix the problem, or it might just make an unusable mess. I could also just give up and use the USA version of the game, which would be loaded correctly.

I'm not sure if the CDL parser will work properly if I manually switch the segment data, but if I can keep it in the same format that the loader uses, maybe? I haven't really run into this problem before.



I will say that most modern debugging and reverse engineering tools don't seem to have any concept of bankswitching like this; not surprising, because you don't need it in modern processors or applications, and it's a huge pain in the ass. But it sure makes things difficult.




BONUS FUN FACT: Someone came on TCRF last year claiming to have a prototype of the game, and... well:



:psyduck:

Zamujasa has a new favorite as of 12:03 on Aug 13, 2022

Zamujasa
Oct 26, 2010



Bread Liar
I gave up on fixing it (it turns out I had basically nothing done in the CDL anyway, so time to play through the game again :toot: ). There's a much simpler solution: just replace the ROM header with the US version. It won't work because, well, it's not actually MMC1, but the IDA loader doesn't know and doesn't care, and it's not like it will impact anything; the loader doesn't have any capability of actually understanding mapper operations, just that it needs to put certain banks in certain spots in memory.


That said, part of doing what I can for the CDL involves using and triggering the inventory items and as much other crap as I can. I can't get all of it -- I don't enjoy playing this game that much -- but it gave me an excuse to figure out where your current score/gold is and where your ball / the camera is. Turns out that a lot of effects don't actually move the ball itself as much as where it's shown, which makes sense.


I made a quick Lua script that places a red + over the ball position (roughly), as well as... showing score popups when you get points/gold. It's not perfect, since some things give points after a delay (or otherwise delay adding it), but it gives some feedback that's normally invisible. Unless you're opening your inventory constantly, there's no way to see how much you're gaining from various things.


https://twitter.com/xkeepah/status/1558577189825552385


Turns out that score and gold are the same value in memory, so it looks a bit more interesting on the real tables. Oh well. Next video, maybe.

Zamujasa
Oct 26, 2010



Bread Liar
Continuing Pinball Quest, because now I've had my weed and oh fuck it's 3 in the morning.



A surprisingly small amount of this game is made up of program code!



I mean, a lot of it is dead space, too, but it's still surprising; a game like this really feels like there should be more.



Part of that is because, well, there is. Surprise, again. When doing a pass over some of the code to look for any obviously interesting parts, I stumbled across this routine, which is copied to $0000 in RAM:



After a lot of thinking, I managed to get down what it is this routine actually does, and it's kind of a doozy. Part of that is because it's shown here in the wrong place, so the offsets aren't quite right, but I tried to make them readable.

This appears to basically be kind of a virtual machine / scripting language.


  • Save this block of code to memory.
  • Write the address of the start of some VM code to $0003 (the "VM Pointer"), replacing the address part of LDA $FFFF.
  • Jump/JSR to $0000, where this code runs.
  • It then reads the byte at the pointer, and advances the pointer by one, handling rolling over if needed.
  • The value is then doubled. The top bit is moved into the carry bit.
  • If carry is set (i.e. the value was >= $80), it will store the now-shifted value into the low byte of the $C200 jump.
  • If carry is clear (< $7F), it will store the shifted value to the low byte of the $C100 jump.
  • Then it jumps to whatever address happens to be there.

Individual routines can then read more data, and advance the pointer when doing so.


Here is an image of one of these routines:


This routine is called with byte D9, and it reads an additional byte. Your attack power is increased by that, checked for overflow, and then the cycle begins anew. The naming style I went with for these routines is "GlobalJump_IX_A_FancyName"; 'IX' is the index used to call it, 'A' is the amount of additional bytes it reads, and then the fancy name is whatever.


What made me really realize that it's a glorified VM was finding these routines, which are effectively branch instructions, but operating on the VM code. (The convention is just the branch opcode it emulates, with a J instead of a B.)






I also changed the Lua script to show the calls to this VM/script engine, and while it seems pretty light in the menus, in-game there are enough calls to it that it frequently goes off the bottom of the screen:



Actually figuring out what any of these do is going to be Fun. And a few of them weren't caught when I did my CDL, so I also have to figure out if they're really unused or if I just missed something in the game.

Or both. Probably both.

Zamujasa
Oct 26, 2010



Bread Liar
Your AT (attack power) is measured by these circles at the bottom of the screen; defeating an enemy fills half a circle, draining cuts it by half, there are a few enemy attacks that also lower it.



Every half-circle is worth one damage against enemies, though the first point doesn't count. So, 1 damage at minimum, 12 (6 x 2) at max. The strong flippers double it, the devil flippers quadruple it but occasionally make a crunch instead of flipping.



...or at least that's what I thought, turns out it's slightly stupider:



The last two AT points are both worth 12 damage (i.e., 5 1/2 is the same as 6).



I'm still trying to decipher what most of the routines in this do, but it's surprisingly complicated. Part of it is that the game doesn't use fixed memory addresses like most games -- almost everything is done through indirect indexed reads, which makes it difficult to keep track of what's going on where.

Zamujasa
Oct 26, 2010



Bread Liar
still working on isassembling pinball quest, mostly trying to figure out what the various parts of the object/process memory mean. so far:

code:
	proc memory layout:

	00	status flags, maybe?
	01w	Y position (+0x1000)
	03w	X position (+0x1000)

	0B	Y hitbox size (downwards)
	0C	X hitbox size (rightwards)

	20	hit points(?)

	21	three-byte y speed. negative = highest bit set
	24	three-byte x speed
going back to the game and physically drawing information on top of objects helps. i made my lua script also let me drag the ball around, so i can be a true pinball wizard:
https://twitter.com/xkeepah/status/1561844681352220672


i also discovered that the last table can be a real piece of shit. the bottom of the table actually several different drain slots:



there's a bumper in the middle, and while it's very very difficult to end up in one of the outer drains, it is possible... and they made it so that the further outwards the drain you go down, the further back you get sent

https://twitter.com/xkeepah/status/1561846152001032193




given that this was written by TOSE, and they made a lot of other games, i'm starting to wonder if this kind of bytecode / script is in any of their other games.

Zamujasa
Oct 26, 2010



Bread Liar
trying something different where i post these anecdotes to a slightly more public location just for ease of sharing. i've been focusing on athena, a dog shit nes game, for a friend. and when i say dog shit, hoo lordy.


anyway

* athena's cold/warm boot memory initialization

* trying to find map data

* we can't afford 60 frames per second


athena is not a good game

Zamujasa
Oct 26, 2010



Bread Liar
similarly, here's a post about removing the screen flashing from the legend of kage.

kage is a relatively small, early game, so it was pretty simple this time. i wonder if anyone's made a repository or list of flash-removing hacks yet.

Zamujasa
Oct 26, 2010



Bread Liar
*blows off dust, cough, hack* this thing still here? cool. time to settle in and copy over a few things. turns out i liked posting these things in a thread/forum format, who would have guessed


today: splatterhouse: wanpaku graffiti!


mapped to nowhere - original

as a primer, here are the steps i take when doing a disassembly:

1. (if possible) grab a tool-assisted speedrun and enable fceux's code-data logger
2. open the rom in ida and undefine everything
3. use a code-data log import tool written by a russian dude whose name i can never remember
4. scan through the typically-unused memory regions for unexpected access/cross-references

the NES has 64KB of addressable memory. think of it like a long street, with addresses from 0000 to FFFF. you have little inboxes at 0000, 0001, etc, and then nothing from 0800 to 1FFF, etc. the PPU (graphics system) lives at 2000~2007. the sound and gamepad system resides at 4000~401F. extra memory on the cart, if present, lives at 6000~7FFF, and the ROM itself lives from 8000~FFFF.

tracking down accesses and cross-references to these unused areas is useful, because it's a sign that either the disassembler has wrongly flagged some data as code resulting in gibberish, or the game is being weird.

dian shi ma li (or whatever), the bootleg fruit machine earlier in the thread, has some obscure writes/reads around the 40XX area, iirc, likely from some kind of expansion hardware. this game...



this game writes to $5800. you may notice from above that there is nothing at $5800 here. it also isn't gibberish: in this case, this is immediately after the game boots, as part of its initialization routine.

the answer is simple enough: while this game shipped with a Namco 340 mapper, one of Namco's other mappers, the Namco 163, had a register mapped there, for IRQs.

this game just happens to have a vestigial write to set up a mapper it no longer has. harmless, but a neat nugget.


----


i wonder if anybody knows about this - (original)

doing some more searching, i checked for things that read the second player's controller. this is a one-player game, and there are already some known cheats:

* P1 Select on the title screen (disabled): Open "scene select mode"
* Hold P2 Down+A+B and reset: Open sound test
* Hold P1 Up+A+Select and reset: Go to credits
* Pause and press P2 A+B: Refill life (up to 3 times per power-on)

turns out there's a check for P2 A somewhere, too.



the nice thing about having a tool-assisted speedrun (TAS), or other input movie that plays through the game, is that you can just let it play.

i didn't know where this check was, or what it might be triggered by, so i set a breakpoint on that address in the debugger and let the movie play. while it didn't hit during the game itself, i let the game continue running to the credits, and... aha! the debugger snapped at the very end.



turns out that pushing the A button on player 2's controller will show the time it took to complete the game. it seems to include some other things (maybe the credits? :v:) because the TAS is about 19 minutes and this says 21.

i checked cah4e3's page, tcrf, and gamefaqs (including the guide), nothing seems to mention this existing. weird!

i wonder if anyone knew about this cheat before. well, not really much of a cheat, i guess.



----

wow. they look the same. i aska dem, you want joypad update? both of them say yes. how in the hell

pictured below: adventure island 3 (usa) and splatterhouse: wanpaku graffiti (jpn).


if your first thought is "wow, these look the same!": that's because they're made by the same company, who made a host of other games, too. this carries over to a few other memory addresses: both adventure island 3 and splatterhouse store their game mode and submode variables in $0070 and $0071, along with a few other things.

the difference between them, the added call in splatterhouse, is a routine i don't entirely understand yet. but one thing that routine can do is start recording your inputs to $7000, in the middle of the on-cartridge RAM.





this game does not have on-cartridge RAM.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply