Tetris disassembly, Mod framework, Lua unit testing

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
ejona
Posts: 15
Joined: Sun Jul 14, 2019 9:45 pm

Tetris disassembly, Mod framework, Lua unit testing

Post by ejona »

I just submitted my first mod to romhacking.net (it's in the queue) and thought I'd say hi. It is the Actually Useful Statistics mod for Tetris (TAUS) (source) that adds statistics during the game which I found useful for training. It is similar to the stats provided to viewers of CTWC, although it wasn't inspired by them.

I first want to say, "Thank you." Nesdev has been a great source for learning the NES. The wiki is fantastic and reference material by zeroone (Tetris knowledge) and rainwarrior (bootstrapping the project) was super helpful.

The Tetris-specific stuff I did I don't expect many people to be interested in, but some of the other parts may be more widely useful.

For the Sake of Knowledge, I ended up decoding much more of Tetris than necessary. I have ~78% label coverage, all the RAM mapped, tables marked and most decoded, and a pretty good understanding of how it is put together. It seems I should add that information to http://datacrystal.romhacking.net/wiki/ ... S):ROM_map

It was unclear to me how people develop mods. It seems some people may just modify the ROM directly (aided by a tool), and others seem to modify the disassembly, but both approaches seemed weak to me for my task. I ended up using ld65 to produce ips files directly, and I would "hand-code" the IPS hunks (an example). This provided a surgical approach and worked well for me, although if I needed to make widespread changes it would have been far too tedious. I was also aided because there were large sections of unused ROM.

I also wanted unit tests because manual testing was so slow and it was easy to cause regressions. Most of my work was math-y so bugs could be subtle and rarely visible. So I ended up developing a Lua-based testing framework that I found really helpful. It is small, but it became easy to write tests (example) and I was able to use it for benchmarks as well. It uses fceux, although I am eyeing swapping to mesen.

Does that sort of work seem appropriate? Did I miss common approaches? Any interest in using some of the approaches?
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by zeroone »

That decoding looks really useful. By coincidence, over the weekend, I posted this. I think there's 2 snippets of code in there you can reference in your table if it's currently missing.

Regarding your unit tests, if you prefer C, C++, C#, Java, Julia, or Python over Lua, give Nintaco a try.

I don't know how people make mods. In fact, when I tried modding Tetris to alter the playfield height, it screwed up the timing in the game. That's why in the aforementioned article I switched to just doing it in Java.
ejona
Posts: 15
Joined: Sun Jul 14, 2019 9:45 pm

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by ejona »

I didn't read all of what you posted yet, but I'll integrate your new stuff with my system probably next weekend.

Because I got the Lua stuff to appear blocking (with coroutines) it works fairly well. I don't really care about the language too much, but I'm happy to not need another dependency. I could do Java, but honestly Lua is a bit refreshing (even if it causes its own problems; 1-indexed offsets :'( ). I looked at Nintaco before, but I don't remember what I felt about the API; I can look at it again. The emulator doesn't _really_ matter much for what I was doing, and I could contribute to multiple emulators to make it more natural.

I was oven on the Classic Tetris Monthly Discord and your post was brought up with a request to make a mod for it. I made a hacky hack (source). It does nothing by itself, but if you use a Game Genie code to change the handicap size, like AOLPLG, (or an emulator API) then it will observe that and fill in the playfield appropriately.

I wanted quick and dirty, so I filled "playfield" with the curtain tile, which meant it was fair game for line clearing. I don't think it would have any impact on the game speed, since it only runs on game init. But because I abused the playfield, the top line of the handicap area would end up being cleared. After a little bit of time trying to fix that I said "forget it" and just left one block empty. I may still circle back to try to clean it up, maybe next week end.
Attachments
handicap.dist.ips
(57 Bytes) Downloaded 310 times
handicap-0.png
handicap-0.png (4.4 KiB) Viewed 10059 times
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by rainwarrior »

I also find ca65 convenient for making IPS patches. Generally what I'd do is start from a da65 disassembly, edit that, and then when finished turn the changes into an IPS. Placing new segments at the start and end of the replaced data avoids the need to count bytes.

As for unit tests, I recently discovered the sim65 tool that's already part of the cc65 package. Basically a small emulator that lets you make a 6502 program you can run as a command line application, with text in/out and a return value. Generally the idea is to write a main() in C to handle the output, but it's easy to jump to assembly code from there. Example.
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by zeroone »

ejona wrote:I made a hacky hack
Thanks for the IPS.

I made a very similar hacked version and I even used the same curtain tile to fill in the lower space. But, as mentioned, my changes broke the timing. There is a loop that copies the playfield to VRAM. I alterated that loop to only copy of the upper region, which takes less time. Since the game is NMI driven, it's surprising that my change broke the timing.

I'll test out your version when I get a chance.
ejona
Posts: 15
Joined: Sun Jul 14, 2019 9:45 pm

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by ejona »

rainwarrior wrote:Generally what I'd do is start from a da65 disassembly, edit that, and then when finished turn the changes into an IPS.
I guess that's about what I expected. Using segments would help for the larger block of mod code. My main problem is I would want to re-run da65 to mark a table as bytes or addresses, and similar. It would then be annoying to re-integrate the changes.
rainwarrior wrote:As for unit tests, I recently discovered the sim65 tool that's already part of the cc65 package.
That's really cool. For maybe half the mod it's not as helpful, as the mod would obviously depend quite a bit about how the NES works and Tetris overall. But I do have several files (like charting code) that could use this. I had considered "writing my tests in 6502" but I realized I didn't have any real way to get 1) an error code and 2) print failure statements. This gets me both!

Having to pass --target sim6502 is annoying, but understandable, and I don't know how much I will need to fight C being in the same binary. Teaching my Makefile would require the dependency specification to be aware of the tests. My current approach is working fairly well, but I will probably try to rewrite some of the tests like this and see how it goes.
ejona
Posts: 15
Joined: Sun Jul 14, 2019 9:45 pm

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by ejona »

zeroone wrote:But, as mentioned, my changes broke the timing. There is a loop that copies the playfield to VRAM. I alterated that loop to only copy of the upper region, which takes less time. Since the game is NMI driven, it's surprising that my change broke the timing.
I tracked things down more. The reason your change broke the timing is because spawning the next tetrimino ($988E) waits until copyPlayfieldRowToVRAM ($9725) has copied all playfield rows (based on vramRow). Since only 4 rows are copied per frame, decreasing the total number of rows will decrease the number of frames.

And I figured out why my mod had to have the extra blank space. In your notes you mention that the rows [-2, 1] around the piece are checked for line clears. And you also mention that will underflow and wrap around to the end of the playfield ($04FF), but the entire page is initialized to empty spaces so everything is fine. The point I needed to realize is that the row checking overflows as well, when pieces are placed on the bottom row. However, if I modify that code I also have to be careful not to change the number of frames, because only one row is checked each frame (managed by "lineIndex").

Both of the approaches (modify nametable + disable playfield copy vs modify playfield + disable cleared line check) can't short-circuit the loops. Instead, they can do nothing a particular iteration, but it is very important that they run the same number of iterations to maintain timing.
Attachments
handicap.dist.ips
Fixed line clearing, so fully-fills first handicap row
(75 Bytes) Downloaded 309 times
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by zeroone »

ejona wrote:I tracked things down more. The reason your change broke the timing is because spawning the next tetrimino ($988E) waits until copyPlayfieldRowToVRAM ($9725) has copied all playfield rows (based on vramRow). Since only 4 rows are copied per frame, decreasing the total number of rows will decrease the number of frames.
Ah. I suspected it was something like that.

Is possible to alter the menu to enable the user to select the playfield height?
ejona
Posts: 15
Joined: Sun Jul 14, 2019 9:45 pm

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by ejona »

zeroone wrote:Is possible to alter the menu to enable the user to select the playfield height?
As it happens...

This version is probably not as useful for the AI play since it only lets you select 5 different heights (like in Type B), but it is nice for normal gameplay. If you wanted it to be able to choose any height, you could use an approach like I did in my playerid hack (which is part of taus for technical reasons). You press select at the level-selection screen (which is otherwise unused) and you get a number appearing at the top-right of the screen.
Attachments
handicap.dist.ips
In-game selection of handicap height
(113 Bytes) Downloaded 310 times
User avatar
zeroone
Posts: 939
Joined: Mon Dec 29, 2014 1:46 pm
Location: New York, NY
Contact:

Re: Tetris disassembly, Mod framework, Lua unit testing

Post by zeroone »

zeroone wrote:This version is probably not as useful for the AI play since it only lets you select 5 different heights (like in Type B), but it is nice for normal gameplay. If you wanted it to be able to choose any height, you could use an approach like I did in my playerid hack (which is part of taus for technical reasons). You press select at the level-selection screen (which is otherwise unused) and you get a number appearing at the top-right of the screen.
That sounds awesome. I'll try it out when I get a chance.
Post Reply