Hope you enjoyed this one! It took longer to make than the others. Tetris is really enjoying a boom in popularity right now, and I find this particular quirk to be fascinating. Correction: I made an error in Photoshop layers at the 16:50 mark. The logic beginning at address $8045 should be PLA, TAY, PLA, TAX, PLA. The code in the ROM is correct. Some resources: mass:werk's article about 6502 opcodes - www.masswerk.at/nowgobang/2021/6502-illegal-opcodes Meatfigher's Tetris a.i. script - meatfighter.com/nintendotetrisai/#Try_It_Yourself Note the a.i. script I used is an older version. It was revisited here - meatfighter.com/tetrisairevisited/ You may need to modify the scripts to run them in Mesen. The original was designed for FCEUX.
Displaced Gamers nestris video ayooo?? Loved this video. The explanation of why BCD is needed was the best I've seen, just so clear and direct. And the event viewer visualization of the scoring loop taking over was so good. Looking forward to the next one. Just for fun, here's a few bits of trivia (might update this later): - There's actually a second BCD lookup table inside the game code that actually goes all the way up to like 50, which is used on the level select screen. As a result, it's possible to temporarily fix the bugged level indicator with just a couple of game genie codes. - It may seem counterintuitive that singles are the first line clear to crash the game, since adding smaller numbers seems like it would take less work. However, what this means is that the number of carries increases, which is really what matters for measuring runtime. You can try this yourself: using the standard elementary-school right-to-left method of adding numbers, try computing 999999 + 40 on paper, and then try 999999 + 1200; you probably do more "work" for the first one! - It's actually (somewhat) possible to control the value of the corrupted address in the dynamic jump routine that causes the crash. In NMI, the last values written to $00 and $01 are the inputs of the player 3/player 4 joypads through the "famicom expansion port" (so since people don't really use these controllers, the values are typically 0). Thus, if we hold down certain button combinations, we can essentially cause the jump routine to go wherever we want (with subtle restrictions ofc). This means that, to some extent, the crash can be fixed by forcing the corrupted address to be correct!
Thanks! I cut maybe 2500 words from this script during development because it was getting WAY too long (and the video still ended up being super long versus my typical target), but I did think that arbitrary code execution would be a fun topic. I assume using the Famicom expansion port to direct a jump after having "written code" inside the high score table is a possibility. I think some people are already working on attempting something like this.
surprisingly, it's barely possible to achieve ACE without the expansion ports! that said, humans with access to this additional hardware could use it to make it easier to rollover the level counter (or maaaaaybe even do ACE without a TAS, but that seems very unlikely at the moment)
I checked explanations by: Meetfighter, Agamescout, Hydrant Dude, Fractal, Eric, Wiirambo, RGME, Kirjava and others... and I think I slowly start to understand the topic.
just read about stack smashing because that's what happening in the core :P But indeed, Displaced does amazing job with his disassemblies and visual explanations. Till this day i knew that stack's getting damaged but never knew actually why.
I was thinking it was a coincidence that both Retro Game Mechanics Explained and Displaced Gamers made videos related to Tetris. Guess with the popularity jump that's not the case, lol.
Behind the Code day is always a good day! I love that the NEStris community finally reaching the "true kill screen" has shined more light on the classic game and its fun little quirks. It was very cool getting to see the different ways you and Retro Games Mechanics Explained tackled the same topic, and a very fun coincidence that you both finished your videos so close to each other. I especially appreciated the way you visualized the infinite loop logic.
I love that this brings so much more detail to the recent news event of Willis Gibson beating Tetris. Bravo to him-- he had to progress in the game past a point that the designers didn't prepare for anyone reaching. Thank you so much for this video. It was great to watch! ETA: And yes, I was laughing once I caught on to the impending code disaster.
The $x2 opcodes (except $82, $A2, $C2, $E2) on 6502 do stop the CPU and it gets stuck until a RESET - a write-up called "How MOS 6502 Illegal Opcodes really work" explains why this happens, although it requires you to understand quite a bit about how the 6502 works internally.
The short version is that the CPU decodes each instruction into smaller operations and the last operation is "load the next instruction". Invalid x2 instructions, when decoded, do not contain that operation, so the CPU never stops executing them.
@5:15, a great video that covers this already is, Bugs & Glitches of High-Level NES Tetris, by Retro Game Mechanics Explained. Though he skims over the code and a more in depth look would be a wonderful addition to Behind the Code Leveled Up. Possibly a collaboration?
@3:44 interesting fact: storing 0x12 to equal "12" is called BCD (binary coded decimal).. the 6502 actually has built-in support for it.. but the NES doesn't include the full 6502 so they have to do those corrections by hand.
17:29 ackchually, it's PHA for PusH Accumulator to the stack, and PLA for PulL Accumulator :) and TXAand TYA are exchanged fotr TAX and TAY, but rest is accurate. Yes, i don't have much friends
@20:37: wait, you just said the NES only has 2 kB of RAM! "Tacos" has to be a two-terabit asset at least. 😄 Great video! It got me thinking about how people (including me) talk about how software was so much more lightweight in elder days, and while there's an argument to be made there, a lot of the savings was down to using this kind of hack. It works, and on a sufficiently primitive platform it may be the only option, but it'll catch up with you down the road. (See also: Y2K problem, interlaced video, all the character-set disasters . . .)
Initially I've been studying C++ for amateur programming but recently decided to study assembly code, specifically 6502 assembly, to get a better direct understanding of what goes on "under the hood" as it were. Now I'm understanding the code you present in your videos. Even though I could understand the concepts to an abstract degree they're all starting make sense now. These BTC videos really are such a treat, cheers.
I recently learned that the NES's CPU is an exact clone of another chip just with part of it cut out to avoid copyright infringement. The part that got cut out? It would do native binary coded decimal, which would let them just add 0x09 + 0x01 = 0x10 directly. So this video suggests that this issue is indirectly caused by the NES using a cheaper clone chip.
It's not an exact clone. The Ricoh 2A03 does copy all of the 6502 minus the decimal adjustment logic, but it also adds sound processing, joypad input, and DMA all on the same chip.
@EebstertheGreat Thank you for the additional information! So they added all this other stuff, yet they didn't just leave out that one part, instead just cutting the lines.
@@ZipplyZane Brian Bagnall claims that it was scraped off entirely in _The Story of Commodore: A Company on the Edge._ He says he took a look at micrographs himself and confirmed it. That said, some other people online have claimed the circuitry is still there and just disconnected.
"Some would consider crashing a victory" Me as a teen when I delete update data in Hyrule Warriors Legends(Legal behavior on the 3DS system btw) and hunt for odd behavior when putting an updated savefile into 1.0:
A better approach to managing the score would have been to use base 100 encoding, where bytes are confined between 0~99 ($00~$63) when doing arithmetic. Then, a 100-byte lookup table can be used to do the BCD conversion. From what I looked at in the disassembly for Tetris, there are more than enough bytes in RAM to keep track of an internal score and a displayed score up to 999999 (even for 2 players). I may try implementing this along with other bugfixes for this game.
My philosophy has always been to provide internal variables to all "threads" of an NES program, so Main, NMI and IRQ all have locations they can use to talk to each other but other than that NMI and IRQ have their own internal variables including this "temporary workbench" / "register extension" area of zero page so that no such clobbering can occur as in the video. Also it might be helpful if you are trying to make your interrupt handlers faster to not use the stack and just save the A, X, Y registers to designated per-interrupt zero page locations instead that do nothing else but store your registers during the interrupt. It is a few cycles faster than using the stack.
Ever since a 13-year-old player has crashed _Tetris_ (Nintendo R&D1) on NES, the videos on RGME and Displaced Gamers were released about dissecting the game's code and studying the cause of that crash and this really makes _Tetris_ seem like the least competently coded first-party developed NES game.
The moment when the game's programmers only accounted for 31 levels of 256 possible levelvalues...and only made sure 29 of these actually display the correct levelnumber. And even ducked up behavior beyond that.(That is why levelselects exist btw, so people can test levels that no one at the playtesting can naturally get to)
Thank you for your work! This is a much better explanation of this phenomenon than I had seen other places. The one thing I don't understand, though, is why does scoring a double at that point require so much less processing time than adding the score for a single?
All scoring above a single (a double, triple, or Tetris) does not have a value in the tens place. Because of this, the logic for converting the tens place from hexadecimal to decimal never needs to execute. This saves cycles and shortens scoring time.
Ok, I'm happy to see Displaced Gamer talking about this, but I'm still waiting for Thor "I'm the NWC and the first person to document getting past level 29" to comment on where competitive tetris is now.
I believe some in the ROM hacking community have put together a tuned-up version of Tetris that allows for better calculation of scoring, a proper level/line display, and more.
Headlines: "A 13-year-old is the first human to *_officially_* 'beat' Tetris, by crashing it." Displaced Gamers: "It's possible to roll over to level 0."
The NES never ceases to amaze me. Sure modern computers are orders of magnitude more complex and powerful to the point where wasteful poor programming can still be executed reasonably fast, but the NES for what it was had some neat tricks and a generation of programmers who were on a whole different level.
I'm guessing this is also why Dr. Mario can do weird things and/or crash completely if you perform a very large combo that clears a lot of viruses. The game probably ends up taking too long to add up all of the points that the player scores, and then the code gets interrupted at a critical point.
I used an earlier version of the meatfighter script. There is a later script that uses moves that are more realistic. I thought about moving to the revision at one point, but it would have required additional work to make it compatible with Mesen - the emulator that I use.
You're describing packed decimal in the beginning, which is funny because the 6502 normally supports it natively, but the NES disabled those instructions. edit: might be technically called binary coded decimal. When I was an assembly programmer, we just called it packed decimal, but there's apparently something else called densely packed decimal which is different. 2nd edit: IBM calls it packed decimal
Yes, IBM calls it packed decimal, and other sources call it packed BCD. It's one type of BCD, the other being unpacked, where each decimal digit occupies an entire byte. And it's not exactly that the NES removed the decimal logic; rather, they bought a second-source chip from Ricoh called the 2A03 which adds a sound processor, serial joypad polling, and a sort of rudimentary DMA, all of which are very useful for games. The lack of BCD probably wasn't seen as a big deal, and it allowed Ricoh to reduce the cost by avoiding one of MOS Technology's patents (kind of a ridiculous patent too imo). But this is definitely one game where the programmers could have saved some time if the 2A03 had had decimal addition. (FWIW, there aren't any decimal instructions _per se_ except SED to set the decimal flag and CLD to clear the decimal flag in the status register. Those instructions actually still work on the 2A03, but since the decimal adjustment logic has all been removed, you end up with a binary addition or subtraction when you execute ADC or SBC whether decimal mode is enabled or not.)
Nintendo: No one will ever get past level 29 so our work here is done Pro Tetris players: Bruh we can get into the 150s and up what are you talking about?1 Fun fact: Because of the fact that tournament matches were taking a long time to finish (due to a technique called "Rolling") the organizers of the CTWC (Classic Tetris World Championship) implemented something into the game so that the Tournament carts would speed up again at level 39 because otherwise the games would just go...and go...and go. Originally the idea of a "Line Cap" was brought up but the speed mod is what they settled on It's definitely gonna be interesting to see what part of Tetris gets covered next because while I know about WHAT happens with some of the things that will eventually get covered its the actual HOW in the rom that actually fascinates me more so I'll be ready and waiting for the next one of these
I'm pretty sure there's an error at 16:45, the return code does not do what is described. As shown in the video, the return code is "Push A to Stack", "Transfer X to A", "Push A to Stack", "Transfer Y to A", "Push A to Stack", "Push A to Stack", "Return from Interrupt". The correct instructions here should be PLA, TAY, PLA, TAX, PLA, RTI: "Pull A from Stack", "Transfer A to Y", "Pull A from Stack", "Transfer A to X", "Pull A from Stack", "Return from Interrupt".
And to think, all of this could've been avoided if they just spent 18 more cycles (or less, if they set a flag somewhere) at the beginning of the scoring loop to check if it was maxed out
I'm halfway through and you didn't mention WHY the Tetris developers didn't think anyone would get to a high enough level for this to be a problem. Simply put, the game speeds up so much after 20-something levels that regular play is impossible. Using normal controller posture you can't push the d-pad rapidly enough to move a piece over to the side wall before it lands, so you'll never be able to progress any further in the game and you'll quickly get Game Over. High-level Tetris players eventually figured out a technique called Hypertapping, and another one called Rolling, that allows extremely rapid button pushes to overcome this barrier. The Tetris developers couldn't have foreseen how long it would become possible to play Tetris, so it's frankly amazing that the game doesn't crash until 100 levels *past* their theorised maximum level.
Whoever told me that I could hide my subs really destroyed my free time. I watch more from my subs feed than the recommendation panel, but with nearly 600 subs now, I'm always behind. Oh well, I am still working on my two biggest projects, but it may be a while yet before I release either.
Am I the first to realize that they just had to add score[lines] with base_score[lines] when changing level and they'd have the right score to apply without having to emulate multiplication with multiple adds when some line is cleared ?
nice, just watched a similar vid yesterday...been curious about this ever since that kid "beat" the game....always thought the NES verison = Famicom version, interesting to see that isn't the case...so we essentially had 3 versions of the game
how would the NES change if it used a Harvard architecture vs a von Neumann architecture? and could and emulator be made to fake a Harvard architecture for the 6507?
i always find it ironic when NES games use BCD because they're forced to do it in software as the 6502 variant used in the NES had it's BCD functionality removed (because nintendo sucks and they didn't wanted to pay MOS any royalties)
20:59 Really confused by this part. ISC (ISB in the source code) would not crash the CPU in Mesen. But STP (HLT) would, just like you'd expect. Looking at the 0.9.9 source code, all HLT instructions have an addressing mode of AddrMode::None, which is what causes the "Break on Crash" code to run inside of CPU::FetchOperand(). In my own tests, Mesen does break on a CPU crash. (Try Metroid with the ENGAGE RIDLEY MOTHER F---ER code, uncensored. It should trigger a stop as well.) Maybe you experienced a bug? Not sure why you said ISC caused the crash when ISC doesn't crash the emulator. Are you using "crash" to mean two different things here in the video? One meaning that STP halts the emulated CPU and one meaning that ISC breaks the game? Sorry if my comment is hard to follow. I was just really confused by this part of the video and so I wrote this from a confused place. I expect it'd be just as confusing for you to read as I felt when I wrote it. Hopefully something makes sense.
Honestly, I was scratching my head a bit during the crash phase of testing using Mesen. Some of the documentation I was reading on third party websites didn't quite align with what I was seeing in the emulator. For this reason, I included the screenshot of the options I had selected in the emulator, noted that there is a version 2.0 that is in-progress (And I do believe makes changes to this behavior), and pointed viewers to the mass:werk website for a more detailed breakdown of illegal opcodes. I thought that BRK should crash the NES CPU at first. That said, there was an option to break on BRK instructions... and I unchecked that option. The BRK instruction seems to not be very well-documented - OR - it is an example of an instruction that has different behavior depending on which flavor of 6502 processor (on a hardware level) you are using. When I said "we crashed" I was talking about the NES system/processor being emulated - not the emulator software. With only the "Break on CPU crash" option enabled, that ISC line is where the emulator decided to Break. The uncertainty led to me saying, "One way or another, we crashed" prior to giving the address and instruction. It was frustrating enough that I thought of perhaps writing NES software that changes the background color after executing individual, illegal opcodes just to see what the last color would be prior to execution stopping (and that is... super crude). That said - I honestly, didn't want to put in that much time on something rather trivial (as far as the specific crash code versus experiencing "a crash") for this video. For the material on my channel, I thought the specifics of crashing the NES processor go a little too far into the weeds for most viewers here. That said, I also hoped people would bring up details in the comments. I am happy you commented on the subject.
@@DisplacedGamers For sure! I definitely understand that not everyone cares about this level of detail. The reason I care so much about it is because I made my own emulator as a hobby project in the last couple of years and I spent a lot of time in the Mesen source (among other emulators) to see how it behaved in a lot of edge cases. I particularly loved studying and implementing illegal opcodes. I was proud to see HLT/STP working "correctly" (the CPU basically went idle until a reset) in my emulator, and since Mesen is known for being exceptionally accurate to hardware, it was surprising to see that it wasn't behaving as expected in your video. Mesen has definitely behaved differently from third-party documentation in a lot of my own tests. What I've found is that there's a lot of inaccurate/incomplete documentation online because people only get so deep before they abandon the field, and what they leave behind never gets updated. A lot of obscure behavior is never fully documented. The nesdev forums have been helpful, but even they make mistakes, or speak to a different audience which, even when accurate, usually makes the information incomplete or hard to parse. Mesen tried its best to be accurate to hardware, so I imagine a lot of gaps were filled in with test roms. I don't know for sure. I do know that sourmesen was very active on nesdev during Mesen's development. Documentation may say one thing, but if the hardware doesn't match, and you want an accurate emulator, then match the hardware. This is a big reason why I read the code for so many emulators. I never assume their solutions are correct and, since I don't have a way to build and test my own ROMs on actual hardware (I can't afford an Everdrive), I try to find as much info as possible before making changes. Sometimes all I can find is a single comment in the code, when I'm lucky. Despite all that, this is still my first emulation project. It's probably a big mess and doesn't pass nearly as many test roms as other emulators. It is good with a lot of obscure ones though. 😁 Also, I don't have experience with the source code of Mesen 2. I didn't have any interest in SNES emulation. At least not making my own. And crunching multiple emulators together in one project just seemed like it'd be too much trouble to read through. So I just stuck with 0.9.9. As for BRK, at least on the NES, it behaves similarly to an IRQ (jump to $FFFE), except it sets the B status flag before pushing the status register value to the stack. It wouldn't crash either. Anyway, I'm probably an exception coming at this from an emulation perspective while a lot of viewers are either coming as gamers or 6502 developers. So they'd probably care less about how the emulator behaves and more about what caused the crash to begin with. I'm grateful you included that detail. And grateful for your lengthy reply!
@@Nob1ej0n Interesting update on this. I ran Mesen today to begin work on the next Tetris video, and the NES CPU experienced a crash on an STP instruction... and the emulator actually wrote "CPU Crash" in the bottom, yellow, heads-up... thingy (term is escaping me). I don't remember it doing this last time but rather simply treating that ISC breakpoint as just a break of sorts. I may mention this inconsistency in the next video just to go on record with it. Hopefully I can duplicate this STP crash behavior when it comes time to do video capture and screenshots. Heck - I may even set a breakpoint for execution for $0000-$07FF and edit the "RAM code" after break occurs just to make sure it hits attempts to plow through an STP instruction.
@@lis6502 That's not my point. Along the way, a simple, "This is also known as Binary Coded Decimal," would have been sufficient. If you didn't know it was BCD, you are left without a key detail.
@@thewebmachineyeah, you're probably right, let's hope that Displaced reads your comment because i don't buy simple "oh, i've forgotten this in script" ;p
Hope you enjoyed this one! It took longer to make than the others. Tetris is really enjoying a boom in popularity right now, and I find this particular quirk to be fascinating.
Correction: I made an error in Photoshop layers at the 16:50 mark. The logic beginning at address $8045 should be PLA, TAY, PLA, TAX, PLA. The code in the ROM is correct.
Some resources:
mass:werk's article about 6502 opcodes - www.masswerk.at/nowgobang/2021/6502-illegal-opcodes
Meatfigher's Tetris a.i. script - meatfighter.com/nintendotetrisai/#Try_It_Yourself
Note the a.i. script I used is an older version. It was revisited here - meatfighter.com/tetrisairevisited/
You may need to modify the scripts to run them in Mesen. The original was designed for FCEUX.
"Have you ever given much thought to how we count? Probably not."
Programmers screaming right now
Somehow we release videos about the same topic within 24 hours of each other, and they complement each other wonderfully! Awesome work!
both of you do amazing videos!
OMG, RGME!
I enjoyed your video yesterday and was thinking to myself "gee, Displaced Gamers should also do a video on this." And lo and behold!!
It's a rather nice coincidence, to be sure. Both are great videos!
I actually thought it was Displaced Gamers because forgot that I hit the bell on RGME as well
RGME and Displaced Gamers covering Tetris a day apart? Hell ya.
Yep
Thought the same thing. Is there like a Tetris thing in March that I’m not aware of? Just sort of a weird crossover that isn’t a crossover.
@@RobertoVillegas-vincent404A world record for the NES version was broken recently
@@RobertoVillegas-vincent404 Recently a tetris kill screen was hit by a human.
Unfortunate timing but I'll do it again.
As a non-programmer I always appreciate how you visually depict what's happening in game code.
Thanks!
@@DisplacedGamershell, as a programmer I also appreciate the wonderful visualizations. They're brilliantly done.
"I'M a programmer, not a Tetris master." Best line ever!!!!
Displaced Gamers nestris video ayooo??
Loved this video. The explanation of why BCD is needed was the best I've seen, just so clear and direct. And the event viewer visualization of the scoring loop taking over was so good. Looking forward to the next one.
Just for fun, here's a few bits of trivia (might update this later):
- There's actually a second BCD lookup table inside the game code that actually goes all the way up to like 50, which is used on the level select screen. As a result, it's possible to temporarily fix the bugged level indicator with just a couple of game genie codes.
- It may seem counterintuitive that singles are the first line clear to crash the game, since adding smaller numbers seems like it would take less work. However, what this means is that the number of carries increases, which is really what matters for measuring runtime. You can try this yourself: using the standard elementary-school right-to-left method of adding numbers, try computing 999999 + 40 on paper, and then try 999999 + 1200; you probably do more "work" for the first one!
- It's actually (somewhat) possible to control the value of the corrupted address in the dynamic jump routine that causes the crash. In NMI, the last values written to $00 and $01 are the inputs of the player 3/player 4 joypads through the "famicom expansion port" (so since people don't really use these controllers, the values are typically 0). Thus, if we hold down certain button combinations, we can essentially cause the jump routine to go wherever we want (with subtle restrictions ofc). This means that, to some extent, the crash can be fixed by forcing the corrupted address to be correct!
Thanks!
I cut maybe 2500 words from this script during development because it was getting WAY too long (and the video still ended up being super long versus my typical target), but I did think that arbitrary code execution would be a fun topic. I assume using the Famicom expansion port to direct a jump after having "written code" inside the high score table is a possibility. I think some people are already working on attempting something like this.
surprisingly, it's barely possible to achieve ACE without the expansion ports! that said, humans with access to this additional hardware could use it to make it easier to rollover the level counter (or maaaaaybe even do ACE without a TAS, but that seems very unlikely at the moment)
Is there any plan to use controller 3 and 4 for level 255 attempts?
@@MrCheeze I think only fractal has the hardware for it, and he's a little bit interested, but afaik not planning on it anytime soon
I checked explanations by: Meetfighter, Agamescout, Hydrant Dude, Fractal, Eric, Wiirambo, RGME, Kirjava and others... and I think I slowly start to understand the topic.
It is a fascinating and fun topic.
just read about stack smashing because that's what happening in the core :P But indeed, Displaced does amazing job with his disassemblies and visual explanations. Till this day i knew that stack's getting damaged but never knew actually why.
20:37 glad you managed to catch some things RGE didn't in their videos, like RAM storing tacos
I was thinking it was a coincidence that both Retro Game Mechanics Explained and Displaced Gamers made videos related to Tetris. Guess with the popularity jump that's not the case, lol.
Behind the Code day is always a good day!
I love that the NEStris community finally reaching the "true kill screen" has shined more light on the classic game and its fun little quirks. It was very cool getting to see the different ways you and Retro Games Mechanics Explained tackled the same topic, and a very fun coincidence that you both finished your videos so close to each other. I especially appreciated the way you visualized the infinite loop logic.
I love that this brings so much more detail to the recent news event of Willis Gibson beating Tetris. Bravo to him-- he had to progress in the game past a point that the designers didn't prepare for anyone reaching. Thank you so much for this video. It was great to watch!
ETA: And yes, I was laughing once I caught on to the impending code disaster.
The $x2 opcodes (except $82, $A2, $C2, $E2) on 6502 do stop the CPU and it gets stuck until a RESET - a write-up called "How MOS 6502 Illegal Opcodes really work" explains why this happens, although it requires you to understand quite a bit about how the 6502 works internally.
The short version is that the CPU decodes each instruction into smaller operations and the last operation is "load the next instruction". Invalid x2 instructions, when decoded, do not contain that operation, so the CPU never stops executing them.
15:54 Channeling Yoda there. "Never his mind on where he was. Hmm? What he was doing."
I think I found my new favorite phrase, "An absolutely amazing cataclysm of unfortunate events." 😅
I always admire your dedication to the small quirks and details in this historic pieces. Well done, again!
Illegal opcodes is the bread and butter of C64 demoscene coders =P
that's why most of them got arrested at some point.... for executing illegal ... opcodes...
maybe undocumented or unintended opcodes is a better word.
@@Alibaba-id4dw Yeah but that doesn't sound as sexy and exciting. And "Illegal" has an established meaning in computer science context
@@antivanti Hm. Actually I almost never hear "illegal", but I hear "undefined behavior" all the time.
@@Alibaba-id4dw You have "Illegal characters" and "Illegal request" etc. It's a thing.
@5:15, a great video that covers this already is, Bugs & Glitches of High-Level NES Tetris, by Retro Game Mechanics Explained. Though he skims over the code and a more in depth look would be a wonderful addition to Behind the Code Leveled Up. Possibly a collaboration?
@3:44 interesting fact: storing 0x12 to equal "12" is called BCD (binary coded decimal).. the 6502 actually has built-in support for it.. but the NES doesn't include the full 6502 so they have to do those corrections by hand.
Your production quality is top notch, as is your way of making extremely complex things simple(r). Great job!
17:29 ackchually, it's PHA for PusH Accumulator to the stack, and PLA for PulL Accumulator :) and TXAand TYA are exchanged fotr TAX and TAY, but rest is accurate.
Yes, i don't have much friends
Alright then! Just for that - next video I am going to mix 68k and x86 assembly terminology into the explanation. Ha!
@@DisplacedGamers horrifying. reminds me of how the no$ emulators use x86 style assembly for every type of cpu architecture
@20:37: wait, you just said the NES only has 2 kB of RAM! "Tacos" has to be a two-terabit asset at least. 😄
Great video! It got me thinking about how people (including me) talk about how software was so much more lightweight in elder days, and while there's an argument to be made there, a lot of the savings was down to using this kind of hack. It works, and on a sufficiently primitive platform it may be the only option, but it'll catch up with you down the road. (See also: Y2K problem, interlaced video, all the character-set disasters . . .)
Initially I've been studying C++ for amateur programming but recently decided to study assembly code, specifically 6502 assembly, to get a better direct understanding of what goes on "under the hood" as it were. Now I'm understanding the code you present in your videos. Even though I could understand the concepts to an abstract degree they're all starting make sense now.
These BTC videos really are such a treat, cheers.
I recently learned that the NES's CPU is an exact clone of another chip just with part of it cut out to avoid copyright infringement.
The part that got cut out? It would do native binary coded decimal, which would let them just add 0x09 + 0x01 = 0x10 directly.
So this video suggests that this issue is indirectly caused by the NES using a cheaper clone chip.
It's not an exact clone. The Ricoh 2A03 does copy all of the 6502 minus the decimal adjustment logic, but it also adds sound processing, joypad input, and DMA all on the same chip.
@EebstertheGreat Thank you for the additional information!
So they added all this other stuff, yet they didn't just leave out that one part, instead just cutting the lines.
@@ZipplyZane Brian Bagnall claims that it was scraped off entirely in _The Story of Commodore: A Company on the Edge._ He says he took a look at micrographs himself and confirmed it. That said, some other people online have claimed the circuitry is still there and just disconnected.
You and RGME are so damn good, i watched both videos. Great presentation
What's rgme
@@AboveEmAllProduction retro game mechanics explained
(3:11) I believe you're missing an "INC Score_Mid_Byte_54" instruction at the bottom of the left column here.
I'm sorry. Sadly, it isn't the only Photoshop error in this video.
It always make my day watching a new video of the series, thanks!
Impressive, as always. Though I am familiar with all the topics you cover, it is still a pleasure to watch you cover them
Thank you!
"Some would consider crashing a victory"
Me as a teen when I delete update data in Hyrule Warriors Legends(Legal behavior on the 3DS system btw) and hunt for odd behavior when putting an updated savefile into 1.0:
Awesome video. I'd love to see a continuation of the Tetris stuff - that outro felt like a teaser of what is yet to come!
Working on a follow-up right now that uses the tools built in this first episode.
I love knowing way more than I comprehend about things I barely understand thanks!
18:20 I find it interesting that the earliest signs of trouble are... "music routine", "music logic", "sound routine". Audio glitches.
"BOOM... the number 10 is born..." 😂
Heh
Star Trek and Star Wars reference spotted. Very nice. Great video as always!
RGME's and your video have fullfilled my gestalt.
I'd like to thank you for adding properly formatted subtitles. Just want you to know that people notice!
Always appreciate words of affirmation for this.
A better approach to managing the score would have been to use base 100 encoding, where bytes are confined between 0~99 ($00~$63) when doing arithmetic. Then, a 100-byte lookup table can be used to do the BCD conversion. From what I looked at in the disassembly for Tetris, there are more than enough bytes in RAM to keep track of an internal score and a displayed score up to 999999 (even for 2 players). I may try implementing this along with other bugfixes for this game.
My philosophy has always been to provide internal variables to all "threads" of an NES program, so Main, NMI and IRQ all have locations they can use to talk to each other but other than that NMI and IRQ have their own internal variables including this "temporary workbench" / "register extension" area of zero page so that no such clobbering can occur as in the video. Also it might be helpful if you are trying to make your interrupt handlers faster to not use the stack and just save the A, X, Y registers to designated per-interrupt zero page locations instead that do nothing else but store your registers during the interrupt. It is a few cycles faster than using the stack.
Thank you, fascinating and entertaining as always
Thanks, my friend!
These videos are so interesting and well made! Have you ever considered looking into “Gimmick!”?
Ever since a 13-year-old player has crashed _Tetris_ (Nintendo R&D1) on NES, the videos on RGME and Displaced Gamers were released about dissecting the game's code and studying the cause of that crash and this really makes _Tetris_ seem like the least competently coded first-party developed NES game.
The moment when the game's programmers only accounted for 31 levels of 256 possible levelvalues...and only made sure 29 of these actually display the correct levelnumber. And even ducked up behavior beyond that.(That is why levelselects exist btw, so people can test levels that no one at the playtesting can naturally get to)
Thank you for your work! This is a much better explanation of this phenomenon than I had seen other places.
The one thing I don't understand, though, is why does scoring a double at that point require so much less processing time than adding the score for a single?
All scoring above a single (a double, triple, or Tetris) does not have a value in the tens place. Because of this, the logic for converting the tens place from hexadecimal to decimal never needs to execute. This saves cycles and shortens scoring time.
Ok, I'm happy to see Displaced Gamer talking about this, but I'm still waiting for Thor "I'm the NWC and the first person to document getting past level 29" to comment on where competitive tetris is now.
I am totally here for Absolutely Amazing Cataclysms of Unfortunate Events.
I love it when Tetris stores tacos in RAM 🌮
I kinda want to see some of these old games with messy code get rebuilt under the hood.
I believe some in the ROM hacking community have put together a tuned-up version of Tetris that allows for better calculation of scoring, a proper level/line display, and more.
Let's appreciate the low key "yay" at timestamp ~ 3:26 :D
13:03 you might say, unforeseen consequences...
Displaced Gamers in the HOuse!!🔥🔥🔥
I’ve been playing Tetris lately!
0:28 is this the setup for a Kool Aid ad?
It sure is! I only thought about Kool-Aid Man as art for that screen a bit too late.
13:04 That's my desktop wallpaper now.
21:41 How did the 6502 get past all the brk instructions to get to ISC?
thanks for the upload!
I also crash violently when someone foolishly attempts to execute the contents of my taco storage.
Headlines: "A 13-year-old is the first human to *_officially_* 'beat' Tetris, by crashing it."
Displaced Gamers: "It's possible to roll over to level 0."
Love this
Love this channel! Hope you can touch on some Z80 stuff one day with a few Sega Master System games.
Ok !! Ready for a new video now !!!
These videos are awesome.
The NES never ceases to amaze me. Sure modern computers are orders of magnitude more complex and powerful to the point where wasteful poor programming can still be executed reasonably fast, but the NES for what it was had some neat tricks and a generation of programmers who were on a whole different level.
I'm guessing this is also why Dr. Mario can do weird things and/or crash completely if you perform a very large combo that clears a lot of viruses. The game probably ends up taking too long to add up all of the points that the player scores, and then the code gets interrupted at a critical point.
@DisplacedGamers
1. Great Video
2. What did you modify to play the game with an AI script?
It does movements which are impossible on the unmodded ROM.
I used an earlier version of the meatfighter script. There is a later script that uses moves that are more realistic. I thought about moving to the revision at one point, but it would have required additional work to make it compatible with Mesen - the emulator that I use.
the player crashes: the game won
the game crashes: the player won
You're describing packed decimal in the beginning, which is funny because the 6502 normally supports it natively, but the NES disabled those instructions.
edit: might be technically called binary coded decimal. When I was an assembly programmer, we just called it packed decimal, but there's apparently something else called densely packed decimal which is different.
2nd edit: IBM calls it packed decimal
Yes, IBM calls it packed decimal, and other sources call it packed BCD. It's one type of BCD, the other being unpacked, where each decimal digit occupies an entire byte.
And it's not exactly that the NES removed the decimal logic; rather, they bought a second-source chip from Ricoh called the 2A03 which adds a sound processor, serial joypad polling, and a sort of rudimentary DMA, all of which are very useful for games. The lack of BCD probably wasn't seen as a big deal, and it allowed Ricoh to reduce the cost by avoiding one of MOS Technology's patents (kind of a ridiculous patent too imo). But this is definitely one game where the programmers could have saved some time if the 2A03 had had decimal addition.
(FWIW, there aren't any decimal instructions _per se_ except SED to set the decimal flag and CLD to clear the decimal flag in the status register. Those instructions actually still work on the 2A03, but since the decimal adjustment logic has all been removed, you end up with a binary addition or subtraction when you execute ADC or SBC whether decimal mode is enabled or not.)
Hello Chris! Subscribed
OMG MASS WERK I REMEMBER PLAYING THEIR HTML GAMES ON MY 3DS WHEN I WAS YOUNGER OMG LOL
15:55: Nice reference, you made. Yes. Hm.
I think you are the first person that got this. Restored, my faith is.
Nintendo: No one will ever get past level 29 so our work here is done
Pro Tetris players: Bruh we can get into the 150s and up what are you talking about?1
Fun fact: Because of the fact that tournament matches were taking a long time to finish (due to a technique called "Rolling") the organizers of the CTWC (Classic Tetris World Championship) implemented something into the game so that the Tournament carts would speed up again at level 39 because otherwise the games would just go...and go...and go. Originally the idea of a "Line Cap" was brought up but the speed mod is what they settled on
It's definitely gonna be interesting to see what part of Tetris gets covered next because while I know about WHAT happens with some of the things that will eventually get covered its the actual HOW in the rom that actually fascinates me more so I'll be ready and waiting for the next one of these
Does the 6502 processor have native taco handling instructions?
Looks like we got an Antz/Bug's Life situation in our hands xD
Huh, two separate retro game channels covering the same topic within 24 hours.
Yep. Tetris is super popular right now.
I'm pretty sure there's an error at 16:45, the return code does not do what is described. As shown in the video, the return code is "Push A to Stack", "Transfer X to A", "Push A to Stack", "Transfer Y to A", "Push A to Stack", "Push A to Stack", "Return from Interrupt". The correct instructions here should be PLA, TAY, PLA, TAX, PLA, RTI: "Pull A from Stack", "Transfer A to Y", "Pull A from Stack", "Transfer A to X", "Pull A from Stack", "Return from Interrupt".
@@Xaymar Sorry. That is a photoshop error on my part. I wish I could correct it, but it is too late now.
2:38 Why is it called a nibbl- Wait, "nibble" as in "small bite/byte"!
Idk whether to find that clever or groan-worthy...
Two cakes!
Rewatching this video, I really wonder why the developers didn't just like store a lookup table to make the multiplication process faster lol
speedrunners: "what's a race condition?"
what about the 2 other Tetras versions ? Are they programed that poorly like this one ?
I was actually also expecting some moddification of the code to prevent certain crash,but maybe next time😁
Where can I find the code for the tetris bot?
who put tacos in my NES ram? bring me to your manager!!
And to think, all of this could've been avoided if they just spent 18 more cycles (or less, if they set a flag somewhere) at the beginning of the scoring loop to check if it was maxed out
Can you please stop a video on why double dragon 3 won't execute the spin kick so often??
3:26 Yay!
I'm halfway through and you didn't mention WHY the Tetris developers didn't think anyone would get to a high enough level for this to be a problem. Simply put, the game speeds up so much after 20-something levels that regular play is impossible. Using normal controller posture you can't push the d-pad rapidly enough to move a piece over to the side wall before it lands, so you'll never be able to progress any further in the game and you'll quickly get Game Over. High-level Tetris players eventually figured out a technique called Hypertapping, and another one called Rolling, that allows extremely rapid button pushes to overcome this barrier.
The Tetris developers couldn't have foreseen how long it would become possible to play Tetris, so it's frankly amazing that the game doesn't crash until 100 levels *past* their theorised maximum level.
17:01 typo in pulls, you wrote pushes instead.
Whoever told me that I could hide my subs really destroyed my free time. I watch more from my subs feed than the recommendation panel, but with nearly 600 subs now, I'm always behind. Oh well, I am still working on my two biggest projects, but it may be a while yet before I release either.
Am I the first to realize that they just had to add score[lines] with base_score[lines] when changing level and they'd have the right score to apply without having to emulate multiplication with multiple adds when some line is cleared ?
Tetris fans eating good tonight
Tacos!
So well programed, better resets to 0 instead of breaks the game.
nice, just watched a similar vid yesterday...been curious about this ever since that kid "beat" the game....always thought the NES verison = Famicom version, interesting to see that isn't the case...so we essentially had 3 versions of the game
how would the NES change if it used a Harvard architecture vs a von Neumann architecture?
and could and emulator be made to fake a Harvard architecture for the 6507?
i always find it ironic when NES games use BCD because they're forced to do it in software as the 6502 variant used in the NES had it's BCD functionality removed (because nintendo sucks and they didn't wanted to pay MOS any royalties)
Explains why, about that supposed kid broke the world record by crash.
So you're saying that kid _didn't_ beat Tetris?!
Time to make some tacos
I too like to store my tacos in RAM
Szanuję jak poyebany! 😎🍻
20:59 Really confused by this part. ISC (ISB in the source code) would not crash the CPU in Mesen. But STP (HLT) would, just like you'd expect. Looking at the 0.9.9 source code, all HLT instructions have an addressing mode of AddrMode::None, which is what causes the "Break on Crash" code to run inside of CPU::FetchOperand(). In my own tests, Mesen does break on a CPU crash. (Try Metroid with the ENGAGE RIDLEY MOTHER F---ER code, uncensored. It should trigger a stop as well.) Maybe you experienced a bug?
Not sure why you said ISC caused the crash when ISC doesn't crash the emulator. Are you using "crash" to mean two different things here in the video? One meaning that STP halts the emulated CPU and one meaning that ISC breaks the game?
Sorry if my comment is hard to follow. I was just really confused by this part of the video and so I wrote this from a confused place. I expect it'd be just as confusing for you to read as I felt when I wrote it. Hopefully something makes sense.
Honestly, I was scratching my head a bit during the crash phase of testing using Mesen. Some of the documentation I was reading on third party websites didn't quite align with what I was seeing in the emulator. For this reason, I included the screenshot of the options I had selected in the emulator, noted that there is a version 2.0 that is in-progress (And I do believe makes changes to this behavior), and pointed viewers to the mass:werk website for a more detailed breakdown of illegal opcodes.
I thought that BRK should crash the NES CPU at first. That said, there was an option to break on BRK instructions... and I unchecked that option. The BRK instruction seems to not be very well-documented - OR - it is an example of an instruction that has different behavior depending on which flavor of 6502 processor (on a hardware level) you are using.
When I said "we crashed" I was talking about the NES system/processor being emulated - not the emulator software. With only the "Break on CPU crash" option enabled, that ISC line is where the emulator decided to Break. The uncertainty led to me saying, "One way or another, we crashed" prior to giving the address and instruction. It was frustrating enough that I thought of perhaps writing NES software that changes the background color after executing individual, illegal opcodes just to see what the last color would be prior to execution stopping (and that is... super crude). That said - I honestly, didn't want to put in that much time on something rather trivial (as far as the specific crash code versus experiencing "a crash") for this video.
For the material on my channel, I thought the specifics of crashing the NES processor go a little too far into the weeds for most viewers here. That said, I also hoped people would bring up details in the comments. I am happy you commented on the subject.
@@DisplacedGamers For sure! I definitely understand that not everyone cares about this level of detail. The reason I care so much about it is because I made my own emulator as a hobby project in the last couple of years and I spent a lot of time in the Mesen source (among other emulators) to see how it behaved in a lot of edge cases. I particularly loved studying and implementing illegal opcodes. I was proud to see HLT/STP working "correctly" (the CPU basically went idle until a reset) in my emulator, and since Mesen is known for being exceptionally accurate to hardware, it was surprising to see that it wasn't behaving as expected in your video.
Mesen has definitely behaved differently from third-party documentation in a lot of my own tests. What I've found is that there's a lot of inaccurate/incomplete documentation online because people only get so deep before they abandon the field, and what they leave behind never gets updated. A lot of obscure behavior is never fully documented. The nesdev forums have been helpful, but even they make mistakes, or speak to a different audience which, even when accurate, usually makes the information incomplete or hard to parse. Mesen tried its best to be accurate to hardware, so I imagine a lot of gaps were filled in with test roms. I don't know for sure. I do know that sourmesen was very active on nesdev during Mesen's development. Documentation may say one thing, but if the hardware doesn't match, and you want an accurate emulator, then match the hardware. This is a big reason why I read the code for so many emulators. I never assume their solutions are correct and, since I don't have a way to build and test my own ROMs on actual hardware (I can't afford an Everdrive), I try to find as much info as possible before making changes. Sometimes all I can find is a single comment in the code, when I'm lucky.
Despite all that, this is still my first emulation project. It's probably a big mess and doesn't pass nearly as many test roms as other emulators. It is good with a lot of obscure ones though. 😁
Also, I don't have experience with the source code of Mesen 2. I didn't have any interest in SNES emulation. At least not making my own. And crunching multiple emulators together in one project just seemed like it'd be too much trouble to read through. So I just stuck with 0.9.9.
As for BRK, at least on the NES, it behaves similarly to an IRQ (jump to $FFFE), except it sets the B status flag before pushing the status register value to the stack. It wouldn't crash either.
Anyway, I'm probably an exception coming at this from an emulation perspective while a lot of viewers are either coming as gamers or 6502 developers. So they'd probably care less about how the emulator behaves and more about what caused the crash to begin with. I'm grateful you included that detail. And grateful for your lengthy reply!
@@Nob1ej0n Interesting update on this. I ran Mesen today to begin work on the next Tetris video, and the NES CPU experienced a crash on an STP instruction... and the emulator actually wrote "CPU Crash" in the bottom, yellow, heads-up... thingy (term is escaping me). I don't remember it doing this last time but rather simply treating that ISC breakpoint as just a break of sorts.
I may mention this inconsistency in the next video just to go on record with it. Hopefully I can duplicate this STP crash behavior when it comes time to do video capture and screenshots. Heck - I may even set a breakpoint for execution for $0000-$07FF and edit the "RAM code" after break occurs just to make sure it hits attempts to plow through an STP instruction.
Same story on same topic in two days. Nice.
2:28 Why didn't you call it BCD at least once in the video? You refer to it every other way except by its actual name. 🤷🏻♂️😜
because adding #$06 with carry as a workaround to CPU core lacking decimal mode gives more kickass content to the viewers ;)
@@lis6502 That's not my point. Along the way, a simple, "This is also known as Binary Coded Decimal," would have been sufficient. If you didn't know it was BCD, you are left without a key detail.
@@thewebmachineyeah, you're probably right, let's hope that Displaced reads your comment because i don't buy simple "oh, i've forgotten this in script" ;p