/BERGASMS/GBA/06

..

09/08/22

New Goal: I will draw a sprite from the sprite memory.

It has been a couple days! A combination of a busy weekend, wasting an evening or two on Dwarf Fortress and doing a lot of reading of documentation means I haven't done an update for a while. I decided to try and get Mode0 drawing working by only reading the GBA docs. This meant no dissecting existing code and instead having to get a decent understanding of what is going on, which, i think i kinda did. The goal was to draw a sprite from the object memory but while i failed on that front (or rather, didn't yet achieve it) I succeeded on another front.
Once you move away from drawing stuff directly into ram you have to start working with objects, tile maps and palettes. The simplest breakdown i can give is that a palette is just a list of colours, a tile is a picture made up of only those colours, and an object is a reference to a tile with a location and possibly some other attributes.

        //so in this case, 0 = clear, 1 = red, 2 = green, 3 = blue
        palette = [clear, red, green, blue]
        
        //this tile is a red box with a clear centre
        tile1 = [
        1,1,1,1,1,1,1,1,
        1,0,0,0,0,0,0,1,
        1,0,0,0,0,0,0,1,
        1,0,0,0,0,0,0,1,
        1,0,0,0,0,0,0,1,
        1,0,0,0,0,0,0,1,
        1,0,0,0,0,0,0,1,
        1,1,1,1,1,1,1,1,
        ]
        
        //this tile is a green box with a blue line in it
        tile2 = [
        2,2,2,2,2,2,2,2,
        2,0,0,0,0,0,0,2,
        2,0,0,3,3,0,0,2,
        2,0,0,3,3,0,0,2,
        2,0,0,3,3,0,0,2,
        2,0,0,3,3,0,0,2,
        2,0,0,0,0,0,0,2,
        2,2,2,2,2,2,2,2,
        ]
        
        //show a red box at 10,20
        //show a red box at 60,23
        //show a green box with a blue line at 35,41
        object1 = [10,20,tile1]
        object2 = [60,23,tile1]
        object3 = [35,41,tile2]
        
        
Working from the top down through the documentation though, before sprites (objects) were discussed it talked about backgrounds. In terms of drawing stuff they appear to work in much the same way except instead of specifying an x/y location, you provide what is known as a map, which is a grid of tiles that is rendered to the background layer. So using the previous definitions we could do the following.

        
        //this map is two red boxes, then one green box with a blue line, then red boxes for the rest of the bg.
        map = [
            tile1, tile1, tile2, tile1, tile1, tile1, tile1, ....... tile1
        ]
And hey, that is what we get. Which made me really pleased with myself, because i had to do a bunch of stuff to make this happen.

Stuff I Had To Do To Make This Happen

In no particular order! But the first thing I want to talk about is how great Zig is for all this. Consider the BG0CNT Register, this is the register that controls how Background 0 accesses what it needs to draw stuff. Basically where is the tile and map data and how to interpret it. There are 4 of these registers for the 4 backgrounds, all of which are the same shape. They look like this

  Bit   Expl.
  0-1   BG Priority             (0-3, 0=Highest)
  2-3   Character Base Block    (0-3, in units of 16 KBytes) (=BG Tile Data)
  4-5   Not used (must be zero) (except in NDS mode: MSBs of char base)
  6     Mosaic                  (0=Disable, 1=Enable)
  7     Colors/Palettes         (0=16/16, 1=256/1)
  8-12  Screen Base Block       (0-31, in units of 2 KBytes) (=BG Map Data)
  13    BG0/BG1:                Not used (except in NDS mode: Ext Palette Slot for BG0/BG1)
  13    BG2/BG3:                Display Area Overflow (0=Transparent, 1=Wraparound)
  14-15 Screen Size             (0-3)
        
So 16 bits that controls a bunch of stuff depending on what you do with them. And they're not as nice as just any given bit toggles a single thing on or off; some are simple flags, some are values from 0-3, some are 5 bit index values, some are pdding that must not be touched on pain of death, etc. Normally this stuff is a bit of a pain to work with, but NOT with Zig. Consider how we represent the above in Zig code.

pub const BackgroundControl = struct {
    const BGPriority = enum(u2) {
        Low,
        Medium,
        High,
        Highest,
    };

    const BaseBlock = enum(u2) { First, Second, Third, Fourth };

    const Mosaic = enum(u1) { Enabled, Disabled };

    const PaletteSize = enum(u1) { SixteenBySixteen, TwoFiftySix };

    const Overflow = enum(u1) { Transparent, Wraparound };

    const ScreenSize = enum(u2) { Small, Wide, Tall, Large };

    pub const BGControl = packed struct {
        priority: BGPriority = .Low,
        character_block: BaseBlock = .First,
        padding: u2 = 0,
        mosaic: Mosaic = .Disabled,
        palettesize: PaletteSize = .TwoFiftySix,
        screen_block: u5,
        padding_two: u2 = 0,
        overflow: Overflow = .Transparent,
        size: ScreenSize = .Small,
    };

    const BGControl0 = @ptrCast(*BGControl, Mem.BG0Control);
    const BGControl1 = @ptrCast(*BGControl, Mem.BG1Control);
    const BGControl2 = @ptrCast(*BGControl, Mem.BG2Control);
    const BGControl3 = @ptrCast(*BGControl, Mem.BG3Control);

    pub const WhichBackground = enum { Zero, One, Two, Three };

    pub inline fn set(which: WhichBackground, to: BGControl) void {
        switch (which) {
            .Zero => {
                BGControl0.* = to;
            },
            .One => {
                BGControl1.* = to;
            },
            .Two => {
                BGControl2.* = to;
            },
            .Three => {
                BGControl3.* = to;
            },
        }
    }
};
            
And this isn't even good Zig code, this is MY Zig code, i'm a beginner, but this sparks joy, because it can be read like the documentation, but it works like the binary representation with very little stuffing about!!!!! Zig gives us the lovely power to enumerate with descriptive names all of the various bit flags. Then we put it all together into a packed struct which signals to zig to not mess with order and padding, just give it to us exactly how we define it. Then the process of sticking this behemoth into memory is disgustingly slick.

const BGC = @import("Screen.zig").BackgroundControl;

...

    BGC.set(.Two, .{
        .priority = .Highest,
        .character_block = .First,
        .palettesize = .TwoFiftySix,
        .screen_block = 1,
        .size = .Small,
    });

            
And that's it. We get whatever sensible defaults we specified, we can read this clearly and understand what it is specifying, and we don't have to shift a single 0x1 or mask any 0xFF7F's or whatever. If we wanted to level the readability we could also force the user to specify all the values except for the padding to ensure they have to define them. BTW the above basically says set the second background layer to highest priority, use the first block of 16k memory for storing tiles, interpret the palette memory as 256 colours instead of 16 x 16 colours (8 bit colour), start reading the memory 1kb from the start of VRAM for our screen block (the map), and interpret the overall map size as small (as opposed to tall, wide, or large which have different layout interpretations).
I had a brief fight with the Zig compiler while trying to pass an array for the Palette control code I wrote. Basically the 8bit palette is an array of length 255 (its 256 values but the first one is the clear colour). I defined this as follows, more as an exerice is programming.

pub const PaletteControl255 = packed struct {
        clearColour: u16 = 0x0000,
        palette: [255]u16,
    };
And I wanted to be able to pass in the clear colour and then the array of palette colours seperately, and have it composed into the one thing and then stuck into memory. This ended up looking like the following.

    const BGPalette = @ptrCast(*PaletteControl255, Mem.BGPalette);
    pub fn setBGPalette(clearCol: u16, palette: []const u16) void {
        var p: PaletteControl255 = .{
            .palette = [_]u16{0} ** 255,
        };
        p.clearColour = clearCol;
        var i: u32 = 0;
        while (i < palette.len) : (i = i + 1) {
            p.palette[i] = palette[i];
        }
        BGPalette.* = p;
    }
    
And it gets called as follows


    //palette is red, green, blue
    const pal = [_]u16{ 0x001F, 0x0780, 0xF800 };
    //the first item is the clear colour, which we are setting to 0x18C3
    //so in memory the Palette will look like [0x18C3, 0x001F, 0x0780, 0xF800]
    BGPalette.setBGPalette(0x18C3, &pal);
    
So basically palette is an array of zero values and we iterate over the passed in values to set the ones we want and that is great. It works but oh my did i have trouble finding out how i was meant to express this. Not the sort of trouble you have with the Rust compiler, but still trouble. The resourse I found that helped me get it was the Zig crash course which has a superb breakdown of types and arrays/slices of types and what they mean.
Vim! I have been forcing myself to only use Vim. A few things have been coming back to me from whe I used to use it for C programming. I remembered `ctrl+W N` for being able to use the embedded terminal like a normal vim window, that was good. I have also been getting back into the hang of navigating using * to find occurances of the item under the cursor, using w, bw, dw, yy, etc to move around words and yank lines, and using visual mode in a sort of limited sense. Once I am comfortable jumping around the place I will start adding in new learnings. I want to get the hang of usings the various buffers for copying more than one thing at a time. But that's enough for today, tomorrow we Sprite!

Ongoing Goal: I will draw a sprite from the sprite memory.