Mode 7 is a BG mode that allows a background to be rotated, scaled, and skewed. There are more possibilities when HDMA gets involved, Mario Kart. Let's get started with the basics.

Prerequisites

You need to understand fixed point complementary math. You might be able to get by this part but it's going to come into play a lot in the other parts.

Initialization

You remember register $2105 to set the Screen Mode. Set the mode bits to 7, none of the other bits are acknowledged.

Register $211A: Mode 7 Initial Settings (W/1b)
ss----vh  ss: Screen Out Settings (when outside screen area)
                00 = screen repetition
                10 = Color 0 backdrop
                11 = tile 0 repetition
          vh: Screen Flip
                00 = no flip
                01 = horizontal flip
                10 = vertical flip
                11 = both


Register $211B-$211E: Matrix Parameters A-D (W/2b)
low byte  = represents fraction
high byte = represents whole number

$211B-$211E take numbers in 8.8 fixed point format, allowing fractional accuracy. Next up, the registers for the center coordinate where the transformations are performed from.

Register $211F-$2120: Center Coordinate X/Y (W/2b)
---ccccc cccccccc  c: coordinate

Write 2 bytes to $211f for the X center coord, and 2 bytes to $2120 for the Y coord.

This is the part where you want to pay attention. I'm not going to throw a lot of theory at you don't worry. To use mode7 you use this matrix:

θ: Rotation angle
α: X scale factor
β: Y scale factor

[ cos(θ)·(1/α)  -sin(θ)·(1/α) ]  [ A  B ]
[ sin(θ)·(1/β)   cos(θ)·(1/β) ]  [ C  D ]

Note: Rotation is counter-clockwise. For clockwise rotation, you need to put a -sin in C +sin in B.

Rotation and scaling is the simplest to get started with. Simply choose values for θ, α, β. For example, if you wanted the background to be the same size and have it rotate you would update the cos and sin values while multiplying by a scale factor of 1 (no change).

Scaling

This is easiest to get started with. For now let's scale the bg with no rotation, θ = 0. This is nice and easy because cos(0) = 1 and sin(0) = 0, so we know right off the bat that B and C are going to be 0.0 and A and D are going to be 1 * our scale factors, or simply the scale factors themselves. Let's make it really easy. Using a scale factor of 1:

[ 1  0 ]  Identity
[ 0  1 ]   Matrix

This will give you the usual looking background, so yeah, not so interesting yet, but this is helping you understand the concept. Let's spice things up (not so much).

Let's create variables to hold our scale factors, we'll call them sx and sy. They will be worked with in fixed 8.8 format, so they are 16bit variables. For example:

; 16-bit accumulator
lda #1<<8    ; or lda #$100
sta sx
sta sy

The matrix interprets $0100 as 1.00. What confused me when I was learning is that you cannot think of the fractional part like our normal decimal system. This fraction has a range of 0-$ff, so 1.1 and 1.10 are different values!

Mode7 Graphics

Normal (not EXTBG) Mode7 graphics are stored in VRAM differently. The map and tile data are interleaved - the map is stored in every low byte and the tile data in every high byte. Remember, VRAM addresses are 2 bytes in size.

Image Conversion

I've been using Neviksti's pcx2snes to convert images to mode7 format. Mode7 backgrounds are 256-color 128x128 tilemaps which is 1024x1024 pixels. To convert a 1024x1024 pcx just do a pcx2snes -screen7 <image> This will create 3 files: .clr color palette, .pc7 tile data, and .mp7 map data.

Uploading to VRAM

As I was saying earlier, the map is stored in every low byte and the tiles in every high byte. We can use $2115 to change whether the VRAM address is incremented after an upload to the low or high byte in VRAM. What we'll do is setup 2 DMAs to VRAM for the map and the tiles:

;-- Load map data to vram --
stz $2115	; increment vram address after write to $2118 (low byte)
ldx #$0000	; vram addr $0000
stx $2116
ldx #$1800	; Write to $2118
stx $4300
ldx #Map
stx $4302
lda #:Map
sta $4304
ldy #$4000	; # of bytes
sty $4305

lda #$01	; Enable DMA channel 0
sta $420B

;-- Load tile data to vram --
lda #$80	; increment vram address after write to $2119 (high byte)
sta $2115	
ldx #$0000	; vram addr $0000
stx $2116
ldx #$1900	; Write to $2119
stx $4300
ldx #Tiles
stx $4302
lda #:Tiles
sta $4304
ldy #$1f80	; # of bytes
sty $4305

lda #$01
sta $420B

Just for the sake of demonstration I wrote a simple program that lets you scale the width and height of a background as well as scroll through it. It's not much but I thought it would help you learn the basics. Also note that I left the centerX and centerY at 0. Feel free to change that and experiment with it.

Complete Source Code: [[mode-7-tutorial-scaling.7z:file]]

Tutorial by [[bazz]]