(Link to video)

Game objectives

  • Emulator used: lsnes rr2-beta23
  • Major skip glitch
  • Final boss skip glitch
  • Executes arbitrary "code"

Level-up glitch

In a recent video recent video the japanese speedrunner わいなぎ (@Ynagi_akz) performs a glitch allowing to access the debug room of Seiken Densetsu 3. This glitch is triggered first leveling up and delaying the lvl-up screen by going forth and back between two rooms. Thereafter, calling Flamie allows to access a corrupted tile on the World Map leading to the debug room. This glitch was reported by わいなぎ on a 2014 video inside the Japanese community, but this video (and its author) do not seem to be available anymore. This alone saves about 1 hour in the speedrun, but we wanted to analyze this glitch to save even more time and ultimately reach the endgame without calling Flamie.

Script engine

The game uses a script engine, with script code being able to be executed by 16 different threads. A description of this engine can be found on Data Crystal website. Each thread has its own context and memory area, which includes a local stack to store values and addresses, and call stack to store return addresses when sub-routines are executed. Both stacks share the same memory region of 0x100 bytes. The local stack starts at the beginning of this region and grows upward while the call stack starts at the end of this region and grows downward.

What happens

There is very few information on this game's disassembly, so we only could gather partial information on what is going on. When transitioning with a buffered level-up window, the game executes a sub-routine that it doesn't normally. This subroutine is very short, but it has a major flaw: it pushes more data into the local stack than it pulls. So after a transition, the local stack grows by two bytes. After a certain number of transitions, the local stack will intersect with the call stack, and buggy behaviors will start to occur. However, it is extremely difficult to predict what will happen.

The setup

From then, we tried different actions together with different number of screen transitions until we could have a useful behavior. Precisely, we wanted to alter the script pointer so we could execute arbitrary script code. The setup we ended up with was 82 screen transitions and selecting an item in the menu. This has the effect of polling the 24-bit value in $7F1002 as the new script pointer. $7F1002 is among temporary data, but often holds the value $200200 which is inside RAM ($0200). As a consequence, we start executing script code from RAM.
This address is interesting, because there are a few values that we can control in this region. Here is a layout of this memory area:
The area we are interested in is $340-$360 where we can control two bytes using the camera location and one byte by just pressing A at the right time (the fast counter increments by one unit each frame). Our goal is to jump to the controller registers located starting $004218. There are several jump opcodes in the scripting engine. The one we will use is FC which pulls a short address from the local stack and jumps to it. So we need to push the wanted address into the stack. We use the opcode 14 which pushes the following short into the local stack. We can push 0x4200 into the local stack which is just before the controller registers.
The problem is that our setup sends us to address $200, and they are plenty of fixed values in between that would reroute the execution before getting to $340.
The first problem is that there are two unused bytes at $20A and $20B. Default config of lsnes sets all RAM values at startup to 0x55, but it would alter the execution because 55 is a branch opcode. We chose here the default seeded RAM state which works fine (as a side note, Bizhawk's default state works fine too). We consider that the constraints on the values of these two bytes are low enough that it does not violate tasvideos rule about relying on initial ram state. About 7/8 of the opcodes are safe, including the particular values 0x00 and 0xFF.
After a series of safe opcodes, we arrive to the opcode 50 in address $24E, which is a branch instruction. 50 xx branches to current address + (signed) xx. We manipulate this byte so that we branch to $33B. At first look, we cannot independently set both the slow and fast counters to desirable values. However, the slow counter always restarts to 0x40 at the beginning of the room while the fast counter keeps running, so we leave and enter the room after the level-up screen again to sync the both counters to the way we want.
After jumping to $33B, we chose the right camera and counter values as described above to jump to $4200, and after reading several safe openbus values, we arrive at the controller registers at $4218.

The ending code

The credit sequence is started by executing function $FDA8B0. The problem is that the game's script engine does not allow us to execute arbitrary functions, only ones located in specific regions of the $Cx banks. However, we discovered that the game regularly executes a piece of code in RAM! Namely, it does this:
...
$C1/07AE 22 81 03 7F      JSL $7F0381
$7F/0381 5C 1B 74 FD      JMP $FD741B
$FD/741B AD 6E 03         LDA $036E
...
Luckily, the function that we want to execute also lies in $FD bank, so we only have to modify two bytes $7F/0382 and $7F/0383. The script engine allows us to write arbitrary values to arbitrary addresses. The opcode we are interested in is e0 which pulls a 16-bit value and a 16-bit address from the local stack, and write the value into the address (the bank for writing is set to $7F). So the script code that we are executing is the following, which is luckily 8 bytes long:
14 82 03   push 0x0382 into local stack
14 08 80   push 0x8008 into local stack
e0         pulls aaaa then bbbb from local stack, write aaaa into $7Fbbbb
0e         end script
After that, the game eventually executes the code at $7F/0381 which starts the credits.

Route

The chosen character was Kevin. Even if his scenario is not the fastest getting access to fights, he is better to farm exp points as encounters are by sets of two or three and gather quickly around him. We make fights to get 30 xp. We need to use the big room on the left because we need a room where the camera can move -another reason to choose Kevin. Our setup requires an item, this is why we manipulate a chest.

Masterjun: Judging.
Masterjun: Replaced the file with a movie that uses seed 0, which is the default one of BizHawk.
Masterjun: This run uses an lsnes setting of changing the initial memory from the default one. This is generally not allowed in our Movie Rules. However, for compatibility reasons we make an exception if the new initial memory state is the same as the default one of a different accepted emulator. This is the case here.
The movie finishes the game and the viewer feedback was good. Accepted to Moons as a new branch.
Spikestuff: Note for whoever publishes, 97 avi files. Have fun.
Dacicus: I'll try.

TASVideoAgent
They/Them
Moderator
Joined: 8/3/2004
Posts: 14773
Location: 127.0.0.1
Active player (405)
Joined: 2/5/2012
Posts: 1676
Location: Brasil
hopefully you dont have to redo, it was super cool
TAS i'm interested: megaman series: mmbn1 all chips, mmx3 any% psx glitched fighting games with speed goals in general
Player (12)
Joined: 6/17/2006
Posts: 498
Love seeing those weird ACE setups! Yes vote for me! A bit unfortunate that not all initial RAM states work, but it's really cool nonetheless. By the way, why is RAM initialized with 0x55 anyway? I thought it was an Snes9x quirk, so I'm kinda surprised to see that same quirk in lsnes.
Skilled player (1766)
Joined: 5/7/2008
Posts: 187
Location: Japan
I wanted this!!!! Yes vote!!
Dwedit
He/Him
Joined: 3/24/2006
Posts: 692
Location: Chicago
55 and AA are common bit patterns used to test memory, as 55 is 01010101, and AA is 10101010.
Player (12)
Joined: 6/17/2006
Posts: 498
Oh neat, I never realized that! Still, I was pretty sure that this particular RAM initialization wasn't a thing is bsnes, so I'm surprised to see it in a derivative.
Masterjun
He/Him
Site Developer, Skilled player (1968)
Joined: 10/12/2010
Posts: 1179
Location: Germany
As it stands now, this movie has to be rejected due to the rule:
Wiki: MovieRules wrote:
No randomized or unverified custom initial RAM state Emulator settings to initialize RAM a specific way are not allowed unless the entire RAM state as a whole is proven to be a possible starting state for the console being emulated. This also applies to fully random RAM state initialization, which is guaranteed to generate an invalid starting state the vast majority of the time for most consoles.
This rule is kind of silly. While lsnes initializes RAM by alternating 0 and 1 bits (= every byte is 0x55), BizHawk's default initial RAM state uses randomization. So every single SNES BizHawk submission has to be rejected with this rule? Almost. It is fine because it's not a setting. I personally wish this movie would have been done with the lsnes randomization seed of 0 instead of 1, since that would make it equal to BizHawk's initial state, making a good argument for acception instead of rejection. But currently, I would need to reject this because it uses an emulator setting we don't allow. Thoughts on this?
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Site Admin, Skilled player (1234)
Joined: 4/17/2010
Posts: 11251
Location: RU
It's a really dumb situation. Hawk's startup RAM state is the same as lsnes's if in the latter you start a new movie, check "Random initial state", and set RTC to 0. And in hawk you can't disable this. Yet in both emulators it's the same overall RAM state every time you reboot the core. So this movie should work just fine in bizhawk too? I admit it'd be extra dumb to reject this movie due to using hawk's startup state. Last time this thing happened, and Keylie directly mentioned that hawk does it, it was completely ignored (including by me). So yeah, the rule bans all bizhawk movies done with bsnes. Man I need a day to ponder this.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Player (105)
Joined: 11/3/2014
Posts: 9
This issue was already asserted in the FF6 TAS and tasvideos seem to discover this again. Policies are meant to evolve, adapt to reality, generate exceptions, this is a tasvideo-to-tasvideos issue. As far as lsnes, snes9x and bsnes are built, there is no canonical, underneath way to initiate ram without excluding one of these emulators, and of course it does not make any sense to do so.
Emulator Coder
Joined: 3/9/2004
Posts: 4588
Location: In his lab studying psychology to find new ways to torture TASers and forumers
Masterjun wrote:
This rule is kind of silly.
It's not silly. The reasons are described here: http://tasvideos.org/Nach/MemoryInit.html#EmulatorBehavior
Masterjun wrote:
While lsnes initializes RAM by alternating 0 and 1 bits (= every byte is 0x55), BizHawk's default initial RAM state uses randomization.
The latter should not be doing this. Someone should fix it. Some emulators created by authors who mean well but do not understand this subject will initialize RAM with random values. The randomization will cause the game to exhibit different behavior in the emulator each time it is played. Any of those behaviors will probably also differ from any scenario as a whole that is offered by the official platform.
Warning: Opinions expressed by Nach or others in this post do not necessarily reflect the views, opinions, or position of Nach himself on the matter(s) being discussed therein.
Alyosha
He/Him
Editor, Expert player (3514)
Joined: 11/30/2014
Posts: 2713
Location: US
return 0x55555555
seems just as good as
return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320); 
to me. Both are likely equally improbable. Since 00 and FF both work, I think this is good enough. That aside, this game looks cool and that ending was really impressive, I vote yes.
Masterjun
He/Him
Site Developer, Skilled player (1968)
Joined: 10/12/2010
Posts: 1179
Location: Germany
Alyosha wrote:
return 0x55555555 seems just as good as return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320); to me. Both are likely equally improbable.
These are images of initial WRAM, a white pixel is a 0, a black pixel is a 1. This movie uses a setting which makes lsnes memory look very much like BizHawk memory.
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Alyosha
He/Him
Editor, Expert player (3514)
Joined: 11/30/2014
Posts: 2713
Location: US
That's a cool graph, SNES people really have done their research. Thanks for posting that. So looks like 0x55 is valid (or close enough.) And also 0x00 - 0xFF, that's surprising, pretty cool. I wonder if I can force BizHawk to use that alternating 0x00 - 0xFF pattern, that should be enough for this submission, I think I'll try. (Building bsnes is hopeless for me, but maybe I can do something post initialization.)
Site Admin, Skilled player (1234)
Joined: 4/17/2010
Posts: 11251
Location: RU
You can just try running this movie on lsnes while making it use bizhawk's pattern. In seiken3_geg_done.lsmv, open starttime.second and set the value to 0. Also you can build bsneshawk in 1.x branch just for testing what hawk can do.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Alyosha
He/Him
Editor, Expert player (3514)
Joined: 11/30/2014
Posts: 2713
Location: US
WRAM is pokable, so you can easily override the initial 'randomization' even in 2.x Just do this after memory domains are setup:
			for (int i = 0; i <_memoryDomainList> 0)
				{
					if ((i & 1) > 0)
					{
						_memoryDomainList[0].PokeByte(i, 0);
					}
					else
					{
						_memoryDomainList[0].PokeByte(i, 0xFF);
					}
				}
				else
				{
					if ((i & 1) > 0)
					{
						_memoryDomainList[0].PokeByte(i, 0xFF);
					}
					else
					{
						_memoryDomainList[0].PokeByte(i, 0);
					}
				}
			}
I think this correctly recreates the second pattern above (I just tested it and it works as expected.) Seems like a good alternative to the incorrect randomization routine. Is this something we want to do for SNES? Will the run even sync with this 'fix?'
Site Admin, Skilled player (1234)
Joined: 4/17/2010
Posts: 11251
Location: RU
Nach wanted more details on what our emulators actually do, so here it goes. BizHawk running bsnes: https://i.imgur.com/pkdpl5u.png BizHawk running snes9x: https://i.imgur.com/9wYsWvg.png lsnes with default settings: https://i.imgur.com/lrYFR5a.png lsnes with "Random initial state" checked: https://i.imgur.com/irHQ55M.png lsnes with "Random initial state" and "Initial RTC value" changed to both zeros: https://i.imgur.com/RuNh1bv.png For all these cases, initial RAM state remains the same every reboot and changes only after you change the config. As you can see, bizhawk's state is equal to that of lsnes when you set RTC seconds to 0 and enable randomizer in lsnes. This means hawk uses randomizer and 0 RTC too, and the user can't change it. That randomizer generates consistent values every time because it uses RTC as a seed, and that is saved to lsnes movies.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
adelikat
He/Him
Emulator Coder, Expert player, Site Developer, Site Owner (3581)
Joined: 11/3/2004
Posts: 4736
Location: Tennessee
I just wanted to post to say that I think this movie should be published. I have a lot of opinions why. And it may take me awhile to articulate them. In the meantime, just wanted to say I strongly agree with publishing this, and to reject it over it's initial ram state choice is a mistake
It's hard to look this good. My TAS projects
Experienced player (670)
Joined: 11/23/2013
Posts: 2206
Location: Guatemala
Hey, wasn't Seiken Densetsu 3 released officially in the West thanks to a recent collection? I think the localized name is Trials of Mana. If this movie gets published, will it get the new game name?
Here, my YouTube channel: http://www.youtube.com/user/dekutony
Player (105)
Joined: 11/3/2014
Posts: 9
Hi dekutony, Seiken Densetsu 3 was released officially in the West recently indeed. Yet, the intrisic nature of the glitch used here make it strongly bounded to the Super Famicom, original version of the game : even with a translation patch, the way the glitch is performed here would not lead to the end of the game. As far as many improvements are added to the code on the Switch version, it is very likely that this movie will not work.
EZGames69
He/They
Expert player, Publisher, Reviewer (3941)
Joined: 5/29/2017
Posts: 2701
Location: Michigan
the glitch wouldnt have anything to do with the naming. as long as the game has a western release, the game will use the western name. example: http://tasvideos.org/6270S.html this game was only released in Japan on SNES but uses the english name because of the GBA release in the west.
[14:15] <feos> WinDOES what DOSn't 12:33:44 PM <Mothrayas> "I got an oof with my game!" Mothrayas Today at 12:22: <Colin> thank you for supporting noble causes such as my feet MemoryTAS Today at 11:55 AM: you wouldn't know beauty if it slapped you in the face with a giant fish [Today at 4:51 PM] Mothrayas: although if you like your own tweets that's the online equivalent of sniffing your own farts and probably tells a lot about you as a person MemoryTAS Today at 7:01 PM: But I exert big staff energy honestly lol Samsara Today at 1:20 PM: wouldn't ACE in a real life TAS just stand for Actually Cease Existing
Masterjun
He/Him
Site Developer, Skilled player (1968)
Joined: 10/12/2010
Posts: 1179
Location: Germany
dekutony wrote:
If this movie gets published, will it get the new game name?
Yes. [1940] SNES Trials of Mana "2 players" by praetarius3 in 3:24:49.00
Warning: Might glitch to credits I will finish this ACE soon as possible (or will I?)
Player (105)
Joined: 11/3/2014
Posts: 9
? gba? what are you talking about?... Not that i care that much, but as far as there is *huge* differences between Seiken Densetsu 3 and Trials of mana, this is another mistake. Anyway, always happy to provide TASes for games for which the movie doesn't work.
Spikestuff
They/Them
Editor, Expert player, Publisher (2254)
Joined: 10/12/2011
Posts: 6324
Location: The land down under.
Hang on, I got to call up Itoi and tell him that we're calling Mother "Earthbound Zero" and not the official English title "Earthbound Beginnings".
WebNations/Sabih wrote:
+fsvgm777 never censoring anything.
Disables Comments and Ratings for the YouTube account. These colours are pretty neato, and also these.
keylie
He/Him
Editor, Emulator Coder, Expert player (2822)
Joined: 3/17/2013
Posts: 391
Redid the TAS with seed 0: User movie #58685662034132868
Post subject: Movie published
TASVideoAgent
They/Them
Moderator
Joined: 8/3/2004
Posts: 14773
Location: 127.0.0.1
This movie has been published. The posts before this message apply to the submission, and posts after this message apply to the published movie. ---- [4066] SNES Trials of Mana "game end glitch" by keylie, KadMony in 10:54.47