aboutsummaryrefslogtreecommitdiffstats
path: root/src/renderer.c
blob: ec54411d88c720b902dfebf904e44e005fa84bf3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "bd-font.c"

// The frontbuffer is located at the beginning of the VRAM, and requires 20KB of
// video memory for 32 * 20 tiles at 4bpp.
#define FRONTBUF ((u32*)(MEM_VRAM))

// Adjust both of these if the location of the map changes. Each screnblock
// requires less than 2KB.
#define FRONTBUF_TILEMAP ((u16*)(MEM_VRAM + KB(20)))
#define FRONTBUF_SB      10

// The backbuffer is located at the end of the VRAM. This can allow us to use
// more backgrounds but eats into the available memory for sprites. This should
// be fine for non sprite intensive applications. If more sprite memory is
// needed, the backbuffer can be located at the end of the background memory
// instead (64KB - 20KB).
#define BACKBUF  ((u32*)(MEM_VRAM + KB(96) - KB(20)))

// The font data is located at the end of the frontbuffer memory, after the tile
// map and requires 8KB for 256 8x8 characters at 4bpp. This, along with the
// tilemap information allow us to store the frontbuffer and font for a text
// background in the first 2 charblocks (32KB).
#define FONT_DATA    ((u32*)(MEM_VRAM + KB(22)))
#define FONT_TILEMAP ((u16*)(MEM_VRAM + KB(30)))
#define FONT_SB      15

static u32 dirty_tiles[21] = {0};

// TODO: Allow disable bound checking at compile time.
#define BOUNDCHECK_SCREEN() if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return;

IWRAM_CODE
void
draw_pixel(u16 x, u16 y, u8 color) {
    BOUNDCHECK_SCREEN();

    // Find row position for the given x/y coordinates.
    size_t tile_x = x / 8;
    size_t tile_y = y / 8;
    size_t start_col = x % 8;
    size_t start_row = y % 8;
    size_t pos = start_row + (tile_x + tile_y * 32) * 8;

    // Update backbuffer.
    size_t shift = start_col * sizeof(u32);
    BACKBUF[pos] = (BACKBUF[pos] & ~(0xF << shift)) | color << shift;

    // Mark tile as dirty.
    dirty_tiles[tile_y] |= 1 << tile_x;
}

IWRAM_CODE
void
flip_buffer(void) {
    // Copy dirty tiles from the backbuffer to the frontbuffer.
    Tile *dst = FRONTBUF;
    Tile *src = BACKBUF;
    for (size_t j = 0; j < 20; ++j) {
        if (dirty_tiles[j] == 0) {
            continue;
        }
        for (size_t i = 0, k = 1; i < 30; ++i, k <<= 1) {
            if (dirty_tiles[j] & k) {
                dst[i + j * 32] = src[i + j * 32];
            }
        }
        dirty_tiles[j] = 0;
    }
}

static u16 font_map[256] = {0};

void
renderer_init(void) {
    // Initialize display mode and bg palette.
    DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1 | DISP_OBJ;

    // Initialize backgrounds.
    BG_CTRL(0) = BG_CHARBLOCK(0) | BG_SCREENBLOCK(FRONTBUF_SB) | BG_PRIORITY(1);
    BG_CTRL(1) = BG_CHARBLOCK(1) | BG_SCREENBLOCK(FONT_SB) | BG_PRIORITY(0);

    // Use DMA to clear front and back buffers as well as the font memory map.
    dma_fill(FRONTBUF, 0, KB(20), 3);
    dma_fill(FRONTBUF_TILEMAP, 0, KB(2), 3);
    dma_fill(FONT_DATA, 0, KB(8), 3);
    dma_fill(FONT_TILEMAP, 0, KB(2), 3);
    dma_fill(BACKBUF, 0, KB(20), 3);

    // Initialize default palette.
    PAL_BUFFER_BG[0] = COLOR_BLACK;
    PAL_BUFFER_BG[1] = COLOR_WHITE;
    PAL_BUFFER_BG[2] = COLOR_RED;
    PAL_BUFFER_BG[3] = COLOR_BLUE;
    PAL_BUFFER_BG[4] = COLOR_CYAN;
    PAL_BUFFER_BG[5] = COLOR_GREY;

    // Initialize background memory map for frontbuffer and font backgorund.
    for (size_t i = 0; i < 32 * 20; ++i) {
        FRONTBUF_TILEMAP[i] = i;
    }

    // Load font data into VRAM.
    unpack_tiles(&bd_font, FONT_DATA, 256);

    // Initialize the font map translation table. That way we can write
    // a character on the text background layer with:
    //     FONT_TILEMAP[tile_x + 32 * tile_y] = font_map['A'];
    for (size_t i = 0; i < 256; ++i) {
        font_map[i] = 192 + i;
    }
}