Now in this part of the series, we'll actually be.. Working with VRAM. :)
;============================================================================
; LoadBlockToVRAM -- Macro that simplifies calling LoadVRAM to copy data to VRAM
;----------------------------------------------------------------------------
; In: SRC_ADDR -- 24 bit address of source data
; DEST -- VRAM address to write to (WORD address!!)
; SIZE -- number of BYTEs to copy
;----------------------------------------------------------------------------
; Out: None
;----------------------------------------------------------------------------
; Modifies: A, X, Y
;----------------------------------------------------------------------------
;LoadBlockToVRAM SRC_ADDRESS, DEST, SIZE
; requires: mem/A = 8 bit, X/Y = 16 bit
.MACRO LoadBlockToVRAM
lda #$80
sta $2115 ; Set VRAM transfer mode to word-access, increment by 1
ldx #\2 ; DEST
stx $2116 ; $2116: Word address for accessing VRAM.
lda #:\1 ; SRCBANK
ldx #\1 ; SRCOFFSET
ldy #\3 ; SIZE
jsr LoadVRAM
.ENDM
;============================================================================
; LoadVRAM -- Load data into VRAM
;----------------------------------------------------------------------------
; In: A:X -- points to the data
; Y -- Number of bytes to copy (0 to 65535) (assumes 16-bit index)
;----------------------------------------------------------------------------
; Out: None
;----------------------------------------------------------------------------
; Modifies: none
;----------------------------------------------------------------------------
; Notes: Assumes VRAM address has been previously set!!
;----------------------------------------------------------------------------
LoadVRAM:
phb
php ; Preserve Registers
stx $4302 ; Store Data offset into DMA source offset
sta $4304 ; Store data Bank into DMA source bank
sty $4305 ; Store size of data block
lda #$01
sta $4300 ; Set DMA mode (word, normal increment)
lda #$18 ; Set the destination register (VRAM write register)
sta $4301
lda #$01 ; Initiate DMA transfer (channel 1)
sta $420B
plp ; restore registers
plb
rts ; return
;============================================================================
Ok most of these registers we used in the palette routine. Following what
register holds what parameter may be a little confusing. Just suck it up and
remember. It's just memorization. :P
Let me go over the new registers we just used.
Register $2116
i---ffrr i: 0=increment when $2118 or $2139 is accessed
1=increment when $2119 or $2139 is accessed
f: full graphic (?) - Not sure, Yoshi's doc loosely describes it.
r: increment rate: 00=increment by 2 bytes (1x1)
01=increment by 64 bytes (32x32)
10=increment by 128 bytes (64x64)
11=increment by 256 bytes (128x128)
Since $2115 was initialized with #%10000000, the VRAM address is
incremented 1 word after $2119 is accessed. This enables us to write an
entire word to the VRAM address before it is incremented.
Also, the DMA transfer type was set to 2 regs (001), so the DMA alternates
writing between $2118 and $2119. See how it all connects? cool.. :)
Now we need to utilize the macro and routine we made. What we'll do next is
load the block of tile data to VRAM, and tell SNES what addresses will be the
starting addresses for the BG1 character (tile) data, and the BG1 tile map.
Then we'll display the tile on the screen. cool.. :)
Loading the tiles to VRAM is simple now. Just call the LoadBlockToVRAM routine
in your program code. But I hear you ask, "How do I determine the size of the
tile data"? Easy! just use the following calculation:
8 * color_depth(in bits) * number_of_characters
|
what I mean by this is if the BG can have 4 colors a tile, it
is 2 bits in size (00000011 = 4 decimal). so instead of using 4,
you use 2.
So, if we had 20 2bit color tiles. in order to find the size of all of them, we would do
(8*2*20) = $140 bytes (hex). Now you could load the tile data.
\|/ address here is just an example
LoadBlockToVRAM Tiles, $5000, $0140 ; ta da
Ok, in our case we only have 2 tiles, but how do we figure out the color depth?
Well, we need to choose our screen mode first. Let's Check out register $2105
in Qwertie's doc:
Register $2105: Screen mode register (1b/W)
dcbapmmm d: BG4 tile size c: BG3 tile size b: BG2 tile size
a: BG1 tile size Sizes are: 0=8x8; 1=16x16. (See reg. $2107)
p: order of BG priorities m: General screen mode
This register determines the size of the tile represented by 1 entry in the
tile map array, the order that BGs are drawn on the screen, and the screen
mode. The screen modes are:
MODE # of BGs Max Colors/Tile Palettes Colors Total
0 4 4 32 (8 per BG) 128 (32 per BG*4 BGs)
1 3 BG1/BG2:16 BG3:4 8 BG1/BG2:128 BG3:32
2 2 16 8 128
3 2 BG1:256 BG2:16 BG1:1 BG2:8 BG1:256 BG2:128
4 2 BG1:256 BG2:4 BG1:1 BG2:8 BG1:256 BG2:32
5 2 BG1:16 BG2:4 8 BG1:128 BG2:32 (Interlaced mode)
6 1 16 8 128 (Interlaced mode)
7 1 256 1 256
Ah, that's enough copying.
As you can see, each mode has special properties. We'll just use MODE 0,
display the tile on a 32x32 BG1, and use 8x8 tiles.
Note: That reference to $2107 will be talked about soon. Like.. Right now :)
We need to tell the SNES where our tile maps and tile data will be stored.
Registers $2107-$210A, and $210B-$210C, will help us do that.
Register $2107: BG1 Tile Map Location (1B/W)
aaaaaass a: Tile map address s: Screen size: 00=32x32 01=64x32
10=32x64 11=64x64
The a bits set the starting tilemap address. This can be set in intervals of
$0400 words. So setting the a bits to 1 sets the address to $0400,
incrementing that would set it to $0800, etc. If you want to convert the bits
into the address itself to see, shift them left by 10.
(1 left shift 10 = $0400) Since there is only 64K of VRAM, the Most
Significant Bit (bit 7) must be ZERO.
$2108-$210A are the same exact thing as $2107, only for BG2-BG4, respectively.
Register $210B: BG1 & BG2 Character location (1b/W)
aaaabbbb a: Base address for BG2.
b: Base address for BG1.
Register $210C: BG3 & BG4 Character Location (1b/W)
aaaabbbb a: Base address for BG4.
b: Base address for BG3.
The starting address here can be set in intervals of $1000 words. So 0 would
be $0000, 1 would be $1000, etc. Because of the limited size of VRAM, the MSB
must be 0 (you can't go over address $8000). You can convert the bits to the
address value by shifting them left by 12.
Got it? I hope so. Since we're only using BG1, we're going to tell $2107 where
our tile map will be, and $210B where our tile data is. Simple enough right?
lda #$xx
sta $2107
lda #$xx
sta $210B
Ok, let's put our tile data right at the beginning of VRAM, $0000. Let's get
the size of our tile data. (8*2*2 = 32, or $20. We're going to use a 32x32
tile map, so that's 1024 bytes. But wait, each entry in the tile map is 2
bytes long, so now we have a 2048 byte long map. That's $0800 bytes. We'll put
our Tile Map at $0400 (We can't put it lower than that, try shifting 1 left by
10. $0400 is as low as you can go). After swirling all that information around
in your head, you may start to see how the code would look like:
LoadBlockToVRAM Tiles, $000, $0020
stz $2105 ; Set Mode to 0
lda #$04
sta $2107 ; Set BG1's Tile Map VRAM offset to $0400 (word address)
; and the Tile Map size to 32x32
stz $210B ; Set BG1's Character VRAM offset to $0000 (word address)
Remember that the $800 byte-long Tile Map goes from $0400-$0800, cause it makes
$400 words, and each address in VRAM is a word in size.
There's only one thing to do after that. Turn the BG on! We do that with
Register $212C: Main screen designation (1b/W)
---abcde a: Sprites disable/enable.
b: Disable/enable BG4.
c: Disable/enable BG3.
d: Disable/enable BG2.
e: Disable/enable BG1.
So all we need to do is:
lda #$01
sta $212C
THAT'S IT! Well, almost, hehe. The last thing you need to do is tell the
Tile Map to display a character. Let's do that.
Each entry in the Tile Map is worth 2 bytes. Here's the format:
High Low v: vertical flip h: horizontal flip
vhopppcc cccccccc o: priority bit p: palette number
c: Starting character (tile) number
Please read Qwertie's section "The SNES PPU Graphics Organization" for info
on how the palette bits determine what colors the tile will use in CGRAM,
the horizontal and vertical flip bits (which are kind of obvious), and
the priority bit. We'll just be setting the character #.
ur tile # is 1, so all we need to is write to the low byte of some location in
the Tile Map. Let's just display tile on the top-left of the screen (0,0).
Here's the code:
ldx #$0400
stx $2116 lda #$01 sta $2118
and that IS IT. All that's left to do, is write all of this into 1 entire source file. The source is in the next part.
Some Important Notes
Remember that the snes screen is 256x224 pixels in size by default.
Because of this, a screen full of tiles would not be fully visible. 256 pixels
is just enough for 32 8-pixel-long tiles, so how many lines of tiles are we
missing with 224 vertical lines? 224/8 = 28 pixel lines. 32-28 = 4 vertical
lines of tiles. We're missing 4 lines of TILES (not pixels) on the BOTTOM
of the screen. Remember that. You may not have understood my logic,
and if so, just remember the facts.
Also, for some odd reason, the top line of PIXELS is also cut off for tiles on
the first vertical line, like or tile. If we added color to the top line of our
tile, it would not show. You will need to learn how to scroll the BG down a
pixel in order to make the line visible. You will learn this shortly.