Pages: 1
Posted on 06-01-13, 10:06 pm
Super Mario
( ͡° ͜ʖ ͡°)

Karma: 10010
Posts: 3355/4457
Since: 06-08-11
This is stuff I have on the IDA Pro database from quite some time ago but I never quite figured out.

What is HDMA


HDMA means "Horizontal DMA" or "HBlank DMA". DMA means "Direct Memory Access".

DMA is a hardware mechanism that's used to copy data around without having to use the CPU. Normal DMA copies data as fast as it can once turned on. It's used for copying stuff from the gamecard to RAM, or from RAM to VRAM, and so on. It's faster than using the CPU.

DMA has one particular mode, which is called "Start at H-Blank".

To explain what this is, let me explain first how the DS paints its screen.
It paints all the scanlines (1 scanline = 1 row of pixels) from top to bottom. It paints the pixels in every scanline from left to right. When all the scanlines are painted, the DS starts again from the top, drawing the next frame.

The DS graphics engine stays idle (doing nothing) for very little time between drawing every scanline. This little time is called the "HBlank".

Same thing in vertical. Between every frame, the graphics engine stays idle for some time. This is called the VBlank.

So, what happens if we modify the background scrolling registers during an HBlank? The DS has drawn the top part of the screen using the old scrolling values. And it will draw the bottom part using the new values. This is cool: we can change the scrolling per scanline.

And yes, you guessed it right. This is what NSMB does for animating moving clouds and other things in the backgrounds! This is why backgrounds are "cut in half" when getting drawn. This technique is sometimes called "Parallax scrolling" too.

So, back to HDMA. NSMB uses HDMA to change the BG scrolling every HBlank. What HDMA does is basically copy one new value every HBlank. It does it automatically. The CPU turns HDMA on, and then it does its thing. This is useful because it's very important to change the values during the HBlank. If you change them sooner or later, you will get graphical glitches. The CPU could be busy doing other things and miss an HBlank. HDMA never misses an HBlank.

So all the CPU has to do is put all the scrolling values for the next frame in a table in RAM, and turn HDMA on. HDMA will copy one value from the table to the scrolling registers every HBlank automatically.

Where HDMA animation is done



The function 01FFD108 vblankIrqCallback (Which is run on every VBlank) calls 01FFFC20 StageActor__doHdma.

StageActor__doHdma sets up the HDMA thing. There are a few interesting variables in RAM being used here.

02085A7C playerNumber: 0 if mario, 1 if luigi (Or maybe 0 if game host, 1 if not?)
020CAC94 bgParallaxActiveFrame: 0 if HDMA is off, 1 or 2 otherwise (see below).

020CAD00 bottomBGParallaxType
020CAD50 topBGParallaxType
These two hold a number containing the type (?) of parallax scrolling the BG has, or 0 if it doesn't have any.
Also there are two numbers in each variable. One for each player. Depending on which player you are, it will use one or another. I guess Nintendo did it this way in case players were allowed to be in different rooms?

020CAD18 bg1ParallaxDataA
020CAD34 bg1ParallaxDataB
020CAD20 bg2ParallaxDataA
020CAD1C bg2ParallaxDataB
These are pointers to the tables that contain the scrolling data.
NSMB uses two tables per background (A and B). I guess this is so that it draws one while it's modifying the other, and every frame it exchanges them, to avoid graphical glitches. The bgParallaxActiveFrame variable above chooses which of them is used. (1=A, 2=B).

So where do bottomBGParallaxType and topBGParallaxType come from?


This is the interesting bit. Changing these would allow us to change the BG animations.

So, tracing xrefs in IDA Pro I came across this function:
020AEB70 setupBgParallax

It does a shitload of things. Among them it calls these two:
020B1E4C setupBottomBgParallax
020B1D6C setupTopBgParallax

The argument in R2 for them is the top/bottom BG ID.

setupBottomBgParallax does the following:
LDR     R3, =bottomBGParallaxType
[...]
AND     R1, R2, #0xFF
LDR     R0, =bottomBgParallaxTypeTable
MOV     R1, R1,LSL#1
LDRH    R0, [R0,R1]
STRH    R0, [R3,R12] (R12 is playerNumber<<1)


So it takes the Bottom BG ID in R2, ANDs it with 0xFF, looks it up in the table at bottomBgParallaxTypeTable, then stores the result in bottomBGParallaxType.

setupTopBgParallax does the same with another table.

So... I think we found them! The adresses are:
020C7D7C bottomBgParallaxTypeTable
020C7E14 topBgParallaxTypeTable

They're in Overlay 0.

I haven't tested if changing it actually works. Someone please test it!
Posted on 06-02-13, 08:27 am
Death by cuteness

Karma: 6564
Posts: 16/598
Since: 05-01-13
Nice stuff here

Though Nintendo did fail on this one.
Let me explain, there's a way to break completely the HDMA (as in it won't work anymore until the next hardware boot).

How to do ?
_First you should have 2 DSes;
_then you should play MvsL mode;
_you should select at least one course;
_once the course is loaded you can quit and come back to the menu screen;
_now go back in single player mode and select a course which have an animated background;

and you'll see that the background is not animated anymore !
And, depending on the background, it will break the scrolling too, making said background completely static to the camera !
Posted on 06-02-13, 08:39 am
Mariomaster

Karma: 8528
Posts: 258/1681
Since: 06-09-12
--LOL--

If youre right this is an epic bug!
_________________________
GitHub - Kuribo64 - YouTube
Posted on 06-02-13, 08:43 am (rev. 2 by  MeroMero on 06-02-13, 08:44 am)
Death by cuteness

Karma: 6564
Posts: 17/598
Since: 05-01-13
Posted by Mariomaster
--LOL--

If youre right this is an epic bug!


IKR

Of course like all the glitches, I discovered this one by accident (one that I found years ago actually lol) but I thought it wouldn't be revelant until today.
Posted on 06-22-13, 11:30 am


Karma: 228
Posts: 4/22
Since: 10-18-12
HDMA is very useful for scanline 1 and up. Is was created for SNES where the top scanline (scanline 0) is not drawn or visible on the TV screen. Yoshi's Island for SNES uses HDMA for gradient backgrounds (changing backdrop palette RGB on each scanline), perspective texture LUT mapping for big enemies and bosses, and for the Mode 7 perspective rotating island title screen. On SNES that has a kinda slow CPU and addressing mode, having 8 channels of DMA, where each can be setup to use HDMA and the top scanline is never cared for, it is a very cool feature!

However, the GBA changed that into only having 4 DMA channels, and 2 of them are usually used for sound (and other stuff), the GBA draws the top scanline (scanline 0) so its offset needs to be set very quickly when V-Blank interrupt is hit. Games that fail the top scanline on the GBA include Metroid Fusion/Zero Mission, Super Mario Advance 2 - Super Mario World.

However, Super Mario Advance 3 - Yoshi's Island does not fail the top scanline. All the effects that use HDMA works very nicely, except the backdrop! Because YI uses so many tile animations and palette animation per frame, the CPU runs out of time for a frame during heavy calculations like when Yoshi transforms and during the GOAL sequence (Yoshi sends Baby Mario and eggs to the next Yoshi while Goal text is waving), and the gradient backdrop is not updated until a couple of scanlines down on the screen. SNES never seems to fail that (looking at captures from real hardware), even with its slow CPU.

Then we come to the Nintendo DS. Just like GBA, it has 4 DMA channels, and they work in the same way. Only all the data modified and read by the CPU (ARM9) is data/code cached, so it is very very easy to miss the first scanline (scanline 0) during V-Blank. It is also very easy to get fooled you succeeded to update the top scanline, when you actually failed (tricky to actually see the result). New Super Mario Bros. DS succeeds the top scanline (as can be seen in title screen and first level), so it is a pretty well programmed and tested game!

My guess is that HDMA cannot be used during NiFi and WiFi communications, either because of CPU or DMA being blocked, or DMA channels being used for communication? Just think about the multiplayer game where each connected DS is lock-stepping the game frames, the high priority stuff to be processed is game input like touch screen, and there might not be time left to update the top scanline before the screen starts drawing again. I don't really know, but I've tried to make HDMA work during multiplayer (MvsL mode) but the game hang. I know the GBA can't use HDMA during its multiplayer games because of CPU time.

Feel free to test HDMA in NSMBDS some more, and write more about it. I am always interested.
Pages: 1