mode7 like f-zero

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
mode7 like f-zero
by on (#92867)
Hello all,
I'm new here but not new for gba/nds homebrews (http://www.portabledev.com).
Very nice forum with lot's of information regarding SNES.
Some times ago, a french friend wanted to test some snes programming with C source code, so i began to do a snes lib based on snes sdk.
You can see my work here : http://www.portabledev.com/wiki/doku.php
Currently, it works fine with mode1/mode3, scrolling, input, sprite.
Here is my question : I begin to work on mode7 and i can do rotation / scaling but I have no idea to do something like F-ZERO or Mario Kart ...
It seems a little different than on GBA or SNES because I can't use HBL interrupt ...
I did a VBL interrupt like I saw in SNES wiki (to manage A..D Matrix , and so on ..) but i really don't know how can I simulate a ground like in F-ZERO or Mario Kart (or like in Squaresoft Mode 7 Demo).
Please, can someone help me doing such effect ?

by on (#92868)
I'm pretty sure that what they use is HDMA to change the rotation matrix after each scanline. At least that's how the "mode 7" effect is done in GBA mode 1.

by on (#92869)
There is a HBL interrupt on the SNES, but it's rarely used - HDMA is more commonly used, so that you can change the scaling factor each scanline, and make the map progressively larger as it goes down to the screen, for the amazing 3D effect.

You just need to find doccumentation about HDMA - which is DMA that can be programmed to write to registers automatically at the end of the desired scanline (in our case, you'd want to write matrix coefficient).

I'm not sure if the SNES is fast enough to handle interrupts every scan line - especially if programmed in C, so I really think HDMA is a better approach. Interrupt would be the way to go if you only change a register once in the middle of the screen, and it would waste a HDMA channel for only a signle write.

by on (#92880)
thanks for reply, didn't think that HDMA was for Horizontal DMA, so stupid am I :oops:
Will see if http://wiki.superfamicom.org/snes/show/ ... n+the+SNES is useful for that.

by on (#92912)
Also I'm not sure about F-Zero, but Mario Kart used the DSP-1 for it's mode 7 effects.

by on (#92919)
yes, certainly, it required the specfici dsp1 rom with no$sns.
But now, i begin to have something that works with HDMA :).

Image

Just need to know how to add background like sky in FZero, certainly also with HDMA and correct registers.

by on (#92920)
I think for that you change screen settings part way down the screen, perhaps the screen mode itself. You could always watch F-Zero in a debugger to find out.

by on (#92924)
That's right. You can set up a channel to write the BG modes you want to $2105 for different parts of the screen. For a game like F-Zero with mode 1 at the top and mode 7 at the bottom, you only have to change $2105 once in the middle of the frame. The IRQ method can also work well for that since it's only needed once so it won't add any real slowdown and will save you a channel for something else.

by on (#92927)
OK, thanks, will try that.
HDMA is a really great feature for the Snes :)

by on (#92935)
wiiqwertyuiop wrote:
Also I'm not sure about F-Zero, but Mario Kart used the DSP-1 for it's mode 7 effects.

The DSP1 is not necessarly to do mode 7 effects.
I think it was used to speed up the calculations to know the distance between the karts and things like that. I'm not too sure cause I haven't reverse engineered SMK, but I'm sure it's possible to do 3D style effects without using a DSP1.

by on (#92944)
F-Zero had tables in ROM for each angle they wanted visible. The game just points the HDMA source to the entry for the visible angle on screen. The camera only rotates on a single dimension when racing so that helps keep the number of table entries reasonable. It's limiting though depending on what flexibility you want in your game. You wouldn't get away with that method if you wanted something like Pilotwings. Too many tables for each possible angle if you wanted to avoid calculating anything in real time.

Mario Kart had a similar approach even though it had a DSP1. They used the chip to build all the needed mode 7 tables once and stored them all in RAM. They get pointed to for HDMA similar to how F-Zero does it. Even though the DSP1 can do the mode 7 setup faster than the SNES, it still takes a while to actually get the results from the chip into RAM so I'm guessing that's why they precomputed everything. It's doing all of that when the "Nintendo" logo appears on startup if you want to see in a debugger. This method doesn't need a DSP1 obviously and it avoids excessive ROM usage too. Both F-Zero and Mario Kart had the same limits camera rotation so they could get away with this type of shortcut.

by on (#92945)
How many angles do those games use, and how many scanlines are in each table?

by on (#92977)
I haven't touched F-Zero in a while and I forgot what it used. F-Zero's track animation when you rotate is pretty jittery and the sprite animation doesn't look good either so they (understandably) cut corners there compared to SMK. I know SMK had 48K of RAM used for mode 7 plus some extra data for the overhead map view. It gets 128 angles worth of tables from the chip and only saves 2 of the ABCD parameters. Each one is 96 lines large so 128 * 96 * 2 = 24K each or 48K total for the 2 parameters.

by on (#92980)
Does that mean SMK doesn't use the DSP chip after this table is precomputed?

Edit: Nevermind, it apparently does.

by on (#92986)
I don't really understand this approach.
If I were to program something like that I'd definitely do all the calculations in real time.

OK multiplications aren't so fast on 65xxx processors, and you have to compute 4 matrix coefficient for every scanline, but because of the symety of a rotation/scaling matrix, only 2 have to be computed, so there is 128 scanlines that 256 multiplications to do.

However I'm sure there is ways to cheat and makes the multiplications faster, and that way you're not limited in angles and all this stuff.

Also you could change the scaling factor only every 2 scanlines or so. Maybe it wouldn't look as great, but it would half the number of multiplications.

by on (#93000)
MottZilla wrote:
Does that mean SMK doesn't use the DSP chip after this table is precomputed?

Edit: Nevermind, it apparently does.


It still uses it to position sprites based on the current camera position and probably other stuff too.

Bregalad wrote:
I don't really understand this approach.
If I were to program something like that I'd definitely do all the calculations in real time.


If the game design doesn't need extra angles and the RAM (or ROM) is free, it's an easy way to save a lot of frame time. For the racing games mentioned, you'd also have to draw new sprites to account for different angles or else it would like pretty weird too. Even with "cheating", any sort of loop involving math that runs for 100~200ish entries will take a good chunk of frame time to complete even if you unroll everything. It's a lot of time wasted if the memory would otherwise be unused, especially when CPU time can be put towards something that isn't as easy to optimise. Even SMK had a chip to do all of this and it still precomputed everything. SMK itself has very little extra frametime normally so it might have been an optimisation that they made later in development. Not saying there isn't room for optimisation in the game, but it wouldn't be a 60FPS game if they added the mode 7 work on top of everything else.

by on (#93002)
Mario Kart Super Circuit (for Game Boy Advance) cheats. It moves the scroll position at 60 fps but recalculates the rotation table at 30 fps.

by on (#93012)
Also, I must said that my example was made using MATRIX A /D values from this link : http://www.smwcentral.net/?p=viewthread ... =1#p398172

Just one question : how can i manage each matrix A/B/C/D values regarding HBL value ?

I tried to do tables for each line but i don't think it's good (i currently do this code in BVL and only once) :

Code:
      for (i=0;i<160*3;i+=3) {
         unsigned char linehbl = 0x40 + (i/3);
         REG_M7A=(_m7sx & 255); REG_M7A=(_m7sx>>8);
         REG_M7B = _m7sin; _m7mb = REG_MPYMH;
      
         REG_M7A=(j & 255); REG_M7A=(j>>8);
         REG_M7B = -_m7sin; _m7mc = REG_MPYMH;

         REG_M7A=(j & 255); REG_M7A=(j>>8);
         REG_M7B = _m7cos; _m7ma = REG_MPYMH;

         REG_M7A=(j & 255); REG_M7A=(j>>8);
         REG_M7B = _m7cos; _m7md = REG_MPYMH;

         m7_ma[i+1] = _m7ma & 255;m7_mb[i+1] = _m7mb & 255;m7_mc[i+1] = _m7mc & 255;   m7_md[i+1] = _m7md & 255;
         m7_ma[i+2] = _m7ma>>8;m7_mb[i+2] = _m7mb>>8;m7_mc[i+2] = _m7mc>>8;   m7_md[i+2] = _m7md>>8;
      }