-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgba.c
executable file
·173 lines (154 loc) · 4.87 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include "gba.h"
volatile unsigned short *videoBuffer = (volatile unsigned short *) 0x6000000;
u32 vBlankCounter = 0;
/*
Wait until the start of the next VBlank. This is useful to avoid tearing.
Completing this function is required.
*/
void waitForVBlank(void) {
// TODO: IMPLEMENT
// (1)
// Write a while loop that loops until we're NOT in vBlank anymore:
// (This prevents counting one VBlank more than once if your app is too fast)
while (SCANLINECOUNTER > HEIGHT) {
;
}
// (2)
// Write a while loop that keeps going until we're in vBlank:
while (SCANLINECOUNTER < HEIGHT) {
;
}
// (3)
// Finally, increment the vBlank counter:
vBlankCounter++;
}
static int __qran_seed = 42;
static int qran(void) {
__qran_seed = 1664525 * __qran_seed + 1013904223;
return (__qran_seed >> 16) & 0x7FFF;
}
int randint(int min, int max) { return (qran() * (max - min) >> 15) + min; }
/*
Sets a pixel in the video buffer to a given color.
Using DMA is NOT recommended. (In fact, using DMA with this function would be really slow!)
*/
void setPixel(int row, int col, u16 color) {
// TODO: IMPLEMENT
UNUSED(row);
UNUSED(col);
UNUSED(color);
*(videoBuffer + row * WIDTH + col) = color;
}
/*
Draws a rectangle of a given color to the video buffer.
The width and height, as well as the top left corner of the rectangle, are passed as parameters.
This function can be completed using `height` DMA calls.
*/
void drawRectDMA(int row, int col, int width, int height, volatile u16 color) {
// TODO: IMPLEMENT
UNUSED(row);
UNUSED(col);
UNUSED(width);
UNUSED(height);
UNUSED(color);
for (int r = 0; r < height; r++) {
DMA[3].src = &color;
DMA[3].dst = &videoBuffer[OFFSET(row + r, col, WIDTH)];
DMA[3].cnt = width | DMA_ON | DMA_SOURCE_FIXED | DMA_DESTINATION_INCREMENT;
}
}
/*
Draws a fullscreen image to the video buffer.
The image passed in must be of size WIDTH * HEIGHT.
This function can be completed using a single DMA call.
*/
void drawFullScreenImageDMA(const u16 *image) {
// TODO: IMPLEMENT
UNUSED(image);
for (int r = 0; r < HEIGHT; r++) {
DMA[3].src = image + OFFSET(r, 0, WIDTH);
DMA[3].dst = &videoBuffer[OFFSET(r, 0, WIDTH)];
DMA[3].cnt = WIDTH | DMA_ON | DMA_SOURCE_INCREMENT | DMA_DESTINATION_INCREMENT;
}
}
/*
Draws an image to the video buffer.
The width and height, as well as the top left corner of the image, are passed as parameters.
The image passed in must be of size width * height.
Completing this function is required.
This function can be completed using `height` DMA calls. Solutions that use more DMA calls will not get credit.
*/
void drawImageDMA(int row, int col, int width, int height, const u16 *image) {
// TODO: IMPLEMENT
UNUSED(row);
UNUSED(col);
UNUSED(width);
UNUSED(height);
UNUSED(image);
for (int r = 0; r < height; r++) {
DMA[3].src = image + OFFSET(r, 0, width);
DMA[3].dst = &videoBuffer[OFFSET(row + r, col, WIDTH)];
DMA[3].cnt = width | DMA_ON | DMA_SOURCE_INCREMENT | DMA_DESTINATION_INCREMENT;
}
}
/*
Draws a rectangular chunk of a fullscreen image to the video buffer.
The width and height, as well as the top left corner of the chunk to be drawn, are passed as parameters.
The image passed in must be of size WIDTH * HEIGHT.
This function can be completed using `height` DMA calls.
*/
void undrawImageDMA(int row, int col, int width, int height, const u16 *image) {
// TODO: IMPLEMENT
UNUSED(row);
UNUSED(col);
UNUSED(width);
UNUSED(height);
UNUSED(image);
for (int r = 0; r < height; r++) {
DMA[3].src = image + OFFSET(row + r, col, WIDTH);
DMA[3].dst = &videoBuffer[OFFSET(r + row, col, WIDTH)];
DMA[3].cnt = width | DMA_ON | DMA_SOURCE_INCREMENT | DMA_DESTINATION_INCREMENT;
}
}
/*
Fills the video buffer with a given color.
This function can be completed using a single DMA call.
*/
void fillScreenDMA(volatile u16 color) {
// TODO: IMPLEMENT
UNUSED(color);
for (int r = 0; r < HEIGHT; r++) {
DMA[3].src = &color;
DMA[3].dst = &videoBuffer[OFFSET(r, 0, WIDTH)];
DMA[3].cnt = WIDTH | DMA_ON | DMA_SOURCE_FIXED | DMA_DESTINATION_INCREMENT;
}
}
/* STRING-DRAWING FUNCTIONS (provided) */
void drawChar(int row, int col, char ch, u16 color) {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 8; j++) {
if (fontdata_6x8[OFFSET(j, i, 6) + ch * 48]) {
setPixel(row + j, col + i, color);
}
}
}
}
void drawString(int row, int col, char *str, u16 color) {
while (*str) {
drawChar(row, col, *str++, color);
col += 6;
}
}
void drawCenteredString(int row, int col, int width, int height, char *str, u16 color) {
u32 len = 0;
char *strCpy = str;
while (*strCpy) {
len++;
strCpy++;
}
u32 strWidth = 6 * len;
u32 strHeight = 8;
int new_row = row + ((height - strHeight) >> 1);
int new_col = col + ((width - strWidth) >> 1);
drawString(new_row, new_col, str, color);
}