-
Notifications
You must be signed in to change notification settings - Fork 0
/
gba.c
133 lines (104 loc) · 4.57 KB
/
gba.c
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "gba.h" // Mode 0 Scaffold
// The start of the video memory
unsigned volatile short *videoBuffer = (unsigned short *)0x6000000;
// ---- Miscellaneous Functions ----
int collision(int colA, int rowA, int widthA, int heightA, int colB, int rowB, int widthB, int heightB) {
return rowA <= rowB + heightB - 1 && rowA + heightA - 1 >= rowB && colA <= colB + widthB - 1 && colA + widthA - 1 >= colB;
}
void waitForVBlank() {
while (SCANLINECOUNTER >= 160);
while (SCANLINECOUNTER < 160);
}
// ---- Mode 3 ----
void setPixel3(int col, int row, unsigned short color) {
videoBuffer[OFFSET(col, row, SCREENWIDTH)] = color;
}
void drawRect3(int col, int row, int width, int height, volatile unsigned short color) {
for (int r = 0; r < height; r++) {
DMANow(3, &color, &videoBuffer[OFFSET(col, row + r, SCREENWIDTH)], DMA_SOURCE_FIXED | width);
}
}
void fillScreen3(volatile unsigned short color) {
unsigned short c = color;
DMANow(3, &c, videoBuffer, DMA_SOURCE_FIXED | (SCREENWIDTH * SCREENHEIGHT));
}
void drawImage3(int col, int row, int width, int height, const unsigned short *image) {
for (int r = 0; r < height; r++) {
DMANow(3, &image[OFFSET(0, r, width)], &videoBuffer[OFFSET(col, row + r, SCREENWIDTH)], width);
}
}
void drawFullscreenImage3(const unsigned short *image) {
DMANow(3, image, videoBuffer, SCREENWIDTH * SCREENHEIGHT);
}
// ---- Mode 4 ----
void drawImage4(int col, int row, int width, int height, const unsigned short *image) {
for (int i = 0; i < height; i++) {
DMANow(3, &image[OFFSET(0, i, width / 2)], &videoBuffer[OFFSET(col, row + i, SCREENWIDTH) / 2], width / 2);
}
}
void setPixel4(int col, int row, unsigned char colorIndex) {
volatile unsigned short pixelData = videoBuffer[OFFSET(col, row, SCREENWIDTH) / 2];
if (col & 1) {
pixelData &= 0x00FF;
pixelData |= colorIndex << 8;
} else {
pixelData &= 0xFF00;
pixelData |= colorIndex;
}
videoBuffer[OFFSET(col, row, SCREENWIDTH) / 2] = pixelData;
}
void drawRect4(int col, int row, int width, int height, volatile unsigned char colorIndex) {
volatile unsigned short pixelData = colorIndex | (colorIndex << 8);
for (int r = 0; r < height; r++) {
// Ensure we don't DMA 0 copies
if (width == 1) {
setPixel4(col, row + r, colorIndex);
} else if (width == 2) {
setPixel4(col, row + r, colorIndex);
setPixel4(col + 1, row + r, colorIndex);
} else if ((col & 1) && (width & 1)) /* Odd width odd col */ {
setPixel4(col, row + r, colorIndex);
DMANow(3, &pixelData, &videoBuffer[OFFSET(col + 1, row + r, SCREENWIDTH) / 2], DMA_SOURCE_FIXED | (width - 1) / 2);
} else if (width & 1) /* Even col odd width */ {
DMANow(3, &pixelData, &videoBuffer[OFFSET(col, row + r, SCREENWIDTH) / 2], DMA_SOURCE_FIXED | (width - 1) / 2);
setPixel4(col + width - 1, row + r, colorIndex);
} else if (col & 1) /* Odd col even width */ {
setPixel4(col, row + r, colorIndex);
DMANow(3, &pixelData, &videoBuffer[OFFSET(col + 1, row + r, SCREENWIDTH) / 2], DMA_SOURCE_FIXED | (width - 2) / 2);
setPixel4(col + width - 1, row + r, colorIndex);
} else /* Even col even width */ {
DMANow(3, &pixelData, &videoBuffer[OFFSET(col, row + r, SCREENWIDTH) / 2], DMA_SOURCE_FIXED | width / 2);
}
}
}
void fillScreen4(volatile unsigned char colorIndex) {
volatile unsigned short pixelData = colorIndex | (colorIndex << 8);
DMANow(3, &pixelData, videoBuffer, DMA_SOURCE_FIXED | (SCREENWIDTH * SCREENHEIGHT) / 2);
}
void drawFullscreenImage4(const unsigned short *image) {
DMANow(3, image, videoBuffer, SCREENWIDTH * SCREENHEIGHT / 2);
}
void flipPage() {
if (REG_DISPCTL & DISP_BACKBUFFER) {
videoBuffer = BACKBUFFER;
} else {
videoBuffer = FRONTBUFFER;
}
REG_DISPCTL ^= DISP_BACKBUFFER;
}
// ---- DMA ----
// The start of DMA registers
DMA *dma = (DMA *)0x40000B0;
void DMANow(int channel, volatile const void *src, volatile void *dst, unsigned int cnt) {
dma[channel].cnt = 0;
dma[channel].src = src;
dma[channel].dst = dst;
dma[channel].cnt = cnt | DMA_ON;
}
// ---- Sprites ----
// Hides all sprites in the shadowOAM; Must DMA the shadowOAM into the OAM after calling this function
void hideSprites() {
for (int i = 0; i < 128; i++) {
shadowOAM[i].attr0 = ATTR0_HIDE;
}
}