== Working with VRAM - Part I - Palettes ==

I'm getting right down to business in this tutorial, because this is a BIG
topic to cover. Seriously, get ready for a ton of new registers to be
introduced to, and a lot of theory. Ok, no more time for chatter, let's get
started.

Ok, first we need to make our tile. One tile will be blank, and the other will
be the actual tile. We do this because VRAM is initialized to 0, and that means
that the tile entries not used will use tile 0, so we want that blank. Got it?
We're going to use a program called pcx2snes. eKid has recently made a win32
version of pcx2snes (linux users use WINE i guess), and from my adventures, it
gives much better output than Neviksti's old Dos pcx2snes. The only drawback is
that Neviksti's has a lot more features. If eKid wishes to expand on his program,
it will be perfect.

I made a sample pcx file for you. If you want to make your own, just make a
16x8 image, and fill the first 8x8 with black, and the next with whatever you
want. Just make sure the whole pcx does not use more than 4 colors (we'll be
using a 4 color BG). When you're finished, set the image mode to indexed with a
max of 256 colors. Then, save it as tiles.pcx. You will then run pcx2snes. Press
Import .PCX, and open tiles.pcx. Then click Export .INC. Save the resulting file
as tiles(.inc). Now open up tiles.inc. Change the label "UntitledData" to "Tiles,"
and "UntitledPalette" to "BG_Palette." All set!

Since we're filling up most of bank 0 with code, let's fill bank 1 with the
graphics data. We'll need to put the code in its own section, and we already have
the data labeled in tiles.inc in order to get their addresses later.

You generally insert the tile data at the end of your source file. The code will
look like this:

.BANK 1
.ORG 0
.SECTION "TileData"

    .INCLUDE "tiles.inc"

.ENDS

Simple, right?

Now you need to learn how to upload that palette data.

Now, there are actually 2 ways you can change the palette. You could tell
$2121 the color you want to edit, and then write the data from BG_Palette to
$2122. The other method is to use DMA. The main advantage to using DMA is
that you can load data from any bank, where the first method could only load
from the current bank (or maybe you could change the PBR or DBR? Not sure..
But DMA is the best bet). Now, You'll need to learn DMA sometime or another,
so you'll learn right now.

DMA (Direct Memory Access)
Note: I strongly encourage that you read Qwertie's section on DMA Transfer
Basics, he covers everything perfectly. However, if you have any questions,
simply contact me (see Intro for contact info). Do not look at HDMA yet, it's
a ways away from this tutorial.

Ok, if you read the section, you now know that there are 8 seperate DMA
channels, and several registers that need to be written to in order to
initialize the DMA. The most confusing of these registers is the DMA
Control Register ($43?0). Well, the most confusing bits are the first 3, which
determine the transfer type. Let me try to explain it to you after we finish
writing the macro code for loading the palette. Let's write some code.

;============================================================================
;LoadPalette - Macro that loads palette information into CGRAM
;----------------------------------------------------------------------------
; In: SRC_ADDR -- 24 bit address of source data, 
;     START -- Color # to start on, 
;     SIZE -- # of COLORS to copy
;---------------------------------------------------------------------------- 
; Out: None
;----------------------------------------------------------------------------
; Modifies: A,X
; Requires: mem/A = 8 bit, X/Y = 16 bit
;----------------------------------------------------------------------------
.MACRO LoadPalette
    lda #\2
    sta $2121       ; Start at START color
    lda #:\1        ; Using : before the parameter gets its bank.
    ldx #\1         ; Not using : gets the offset address.
    ldy #(\3 * 2)   ; 2 bytes for every color
    jsr DMAPalette
.ENDM


;============================================================================
; DMAPalette -- Load entire palette using DMA
;----------------------------------------------------------------------------
; In: A:X  -- points to the data
;      Y   -- Size of data
;----------------------------------------------------------------------------
; Out: None
;----------------------------------------------------------------------------
; Modifies: none
;----------------------------------------------------------------------------
DMAPalette:
    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

    stz $4300  ; Set DMA Mode (byte, normal increment)
    lda #$22    ; Set destination register ($2122 - CGRAM Write)
    sta $4301
    lda #$01    ; Initiate DMA transfer
    sta $420B

    plp
    plb
    rts         ; return from subroutine
;============================================================================

------------------------------------ END ------------------------------------

Whew! Did you absorb all that? I sure hope so, I tried commenting every line
so you wouldn't have any questions. If you still don't understand, try reading
Qwertie's DMA register references. Yoshi's DMA register descriptions are nice
as well.

About the DMA control register
Let's go over all the bits that are sent to this register. I'll use Qwertie's
Reference as a guide.

NOTE: Qwertie mixed up his descriptions for the f and i bits. I corrected that here.

Register $43?0: DMA Control Register (1b/w)

da-ifttt    d: (DMA only)   0=read from CPU memory, write to $21xx registers
                            1=read from $21xx registers, write to CPU memory
            a: (HDMA only)  Don't need to worry about this bit
                            We're not doing HDMA
            i: 0=increment CPU memory pointer by 1 after every read/write; 1=decrement
            f: fixed CPU memory address (1=fixed; 0=inc/dec depending on bit i)             
            t: DMA/HDMA transfer type

Bit d
  - You can either read from memory and write to $21xx registers, or read from
        $21xx registers and write to mem. Simple enough..
Bit i
  - inc or dec the DMA source address.
Bit f
  - The DMA source address is either inc/dec'd every read/write, or stays the
        same.
bit t
  - this is where it can get a little confusing. Look at Qwertie's examples,
        he has some right below the description of this register.
  - We used transfer type 000. Look at Qwertie's example:

        "The t bits decide the 'mode' of the transfer.  To describe how this works,
        suppose the bytes of data to be transferred are $01 $23 $45 $67 $89, and the
        register to write to is $2118.  Assuming f=0 and i=0:

        Transfer Type           Order of transfer
        000: 1 reg              $01->$2118 $23->$2118 $45->$2118 $67->$2118 $89->$2118
        001: 2 regs             $01->$2118 $23->$2119 $45->$2118 $67->$2119 $89->$2118
        010: 1 reg write twice  $01->$2118 $23->$2118 $45->$2118 $67->$2118 $89->$2118
        011: 2 regs write twice $01->$2118 $23->$2118 $45->$2119 $67->$2119 $89->$2118
        100: 4 regs             $01->$2118 $23->$2119 $45->$211A $67->$211B $89->$2118
        101, 110, 111  unknown behavior

        Transfer modes 0 and 2 appear to be the same, but are different in HDMA mode."

  Understand? You better!!

Do you understand? If not, contact me, I'd be happy to help you out.

Ok that was a lot to take in, but if you still want more, go to Part II to
learn how to load your tiles to VRAM.

back