aboutsummaryrefslogtreecommitdiffstats
path: root/src/renderer.c
blob: 9fe55b22d7a00b2dc20964d3d1fd8a67e8ac9b57 (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
// TODO: For now we pack front/backbuffers together but this make it so that we
// can only use 2 backgrounds. Instead we can move the backbuffer to the end of
// the VRAM. This will give us 3 backgrounds but eats into the available memory
// for sprites but should be fine for non sprite intensive applications.
#define FRONTBUFFER ((u32*)(MEM_VRAM))
#define BACKBUFFER  ((u32*)(MEM_VRAM + KB(96) - KB(20)))

// Adjust both of these if the location of the map changes. Each screnblock
// requires 2K.
#define FRONTBUFFER_TILEMAP     ((u16*)(MEM_VRAM + KB(20)))
#define FRONTBUFFER_SCREENBLOCK 10

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);
    BACKBUFFER[pos] = (BACKBUFFER[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 = FRONTBUFFER;
    Tile *src = BACKBUFFER;
    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;
    }
}

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

    // Initialize backgrounds.
    BG_CTRL(0) = BG_CHARBLOCK(0) | BG_SCREENBLOCK(FRONTBUFFER_SCREENBLOCK);

    // TODO: Initialize other backgrounds if needed.

    // Use DMA to clear front and back buffers.
    dma_fill(FRONTBUFFER, 0, KB(20), 3);
    dma_fill(BACKBUFFER, 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 (size_t i = 0; i < 32 * 20; ++i) {
        FRONTBUFFER_TILEMAP[i] = i;
    }

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