-
Notifications
You must be signed in to change notification settings - Fork 0
/
video.c
302 lines (259 loc) · 8.26 KB
/
video.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include <stdbool.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include <uzebox.h>
#include <spiram.h>
#include <keyboard.h>
#include "mouse.h"
#include "video.h"
#include "xram.h"
#include "tiles.h"
// row selectors
static unsigned char rowsel[] = {
0U, // first line: start at scanline 0
255U // end of list
};
// VRAM rows for 216 lines
#define HIGHEST_VRAM_ROW 26
static m74_mode7_vram_t vramrow[HIGHEST_VRAM_ROW+1];
// tile row VRAM address table
static u16 const vramrow_offs[32U] PROGMEM = {
(u16)(&vramrow[ 0]),
(u16)(&vramrow[ 1]),
(u16)(&vramrow[ 2]),
(u16)(&vramrow[ 3]),
(u16)(&vramrow[ 4]),
(u16)(&vramrow[ 5]),
(u16)(&vramrow[ 6]),
(u16)(&vramrow[ 7]),
(u16)(&vramrow[ 8]),
(u16)(&vramrow[ 9]),
(u16)(&vramrow[10]),
(u16)(&vramrow[11]),
(u16)(&vramrow[12]),
(u16)(&vramrow[13]),
(u16)(&vramrow[14]),
(u16)(&vramrow[15]),
(u16)(&vramrow[16]),
(u16)(&vramrow[17]),
(u16)(&vramrow[18]),
(u16)(&vramrow[19]),
(u16)(&vramrow[20]),
(u16)(&vramrow[21]),
(u16)(&vramrow[22]),
(u16)(&vramrow[23]),
(u16)(&vramrow[24]),
(u16)(&vramrow[25]),
(u16)(&vramrow[26]),
(u16)(&vramrow[26]),
(u16)(&vramrow[26]),
(u16)(&vramrow[26]),
(u16)(&vramrow[26]),
(u16)(&vramrow[26])
};
static u16 under_cursor[8];
static bool showing_cursor = false;
static bool mouse_detected = false;
u16 cursor_x = 384 / 2;
u16 cursor_y = 216 / 2;
u8 keyboard_buffer = 0;
u8 keyboard_mod_buffer = 0;
static u16 framebuffer_addr;
extern bool snesMouseEnabled;
extern u16 mouse_state;
static void uS_ShowCursor() {
if (showing_cursor) return;
showing_cursor = true;
// draw the cursor, saving what's under it
// thanks to my friend mebibytedraco for helping with this!
u16 byte_off = (cursor_y * 384L + cursor_x) / 8L;
u8 bit_off = (cursor_y * 384L + cursor_x) % 8;
for (u8 line = 0; line < 8; line++) {
u8 high_byte = uS_XramReadU8(0, framebuffer_addr + byte_off);
u8 low_byte = uS_XramReadU8(0, framebuffer_addr + byte_off + 1);
u16 word = ((u16)high_byte << 8) | low_byte;
under_cursor[line] = word;
u16 mask = ((u16)res_cursor_mask[line] << 8) >> bit_off;
word &= ~mask;
u16 sprite = ((u16)res_cursor[line] << 8) >> bit_off;
sprite |= word;
uS_XramWriteU8(0, framebuffer_addr + byte_off, sprite >> 8);
uS_XramWriteU8(0, framebuffer_addr + byte_off + 1, sprite & 0xFF);
byte_off += 384L / 8L;
}
}
static void uS_HideCursor() {
if (!showing_cursor) return;
showing_cursor = false;
u16 byte_off = (cursor_y * 384L + cursor_x) / 8L;
for (u8 line = 0; line < 8; line++) {
uS_XramWriteU8(0, framebuffer_addr + byte_off, under_cursor[line] >> 8);
uS_XramWriteU8(0, framebuffer_addr + byte_off + 1, under_cursor[line] & 0xFF);
byte_off += 384L / 8L;
}
}
static void uS_UpdateMouse() {
/*u16 buttons = ReadJoypad(0);
if (buttons & BTN_UP) cursor_y--;
if (buttons & BTN_DOWN) cursor_y++;
if (buttons & BTN_LEFT) cursor_x--;
if (buttons & BTN_RIGHT) cursor_x++;*/
u16 mouse = mouse_state;
s16 mx = cursor_x;
s16 my = cursor_y;
if (mouse & 0x80) {
mx -= (mouse & 0x7F);
if (mx < 0) mx = 0;
} else {
mx += (mouse & 0x7F);
if (mx > 383) mx = 383;
}
if (mouse & 0x8000) {
my -= ((mouse >> 8) & 0x7F);
if (my < 0) my = 0;
} else {
my += ((mouse >> 8) & 0x7F);
if (my > 215) my = 215;
}
cursor_x = (u16)mx;
cursor_y = (u16)my;
}
static void uS_UpdateKeyboard() {
KeyboardPoll();
keyboard_buffer = KeyboardGetKey(true);
keyboard_mod_buffer = KeyboardGetModifiers();
if (!mouse_detected) {
switch (keyboard_buffer) {
case KB_UP: cursor_y -= 2; break;
case KB_DOWN: cursor_y += 2; break;
case KB_LEFT: cursor_x -= 2; break;
case KB_RIGHT: cursor_x += 2; break;
default: break;
}
if ((s16)cursor_x < 0) cursor_x = 0;
if ((s16)cursor_x > 383) cursor_x = 383;
if ((s16)cursor_y < 0) cursor_y = 0;
if ((s16)cursor_y > 215) cursor_x = 215;
}
}
void _emu_whisper(u16 port, u8 value) {
if (port==0x39 || port == 0) { u8 volatile *const _whisper_pointer1 = (u8 *)0x39; *_whisper_pointer1 = value; }
if (port==0x3A || port == 1) { u8 volatile *const _whisper_pointer2 = (u8 *)0x3A; *_whisper_pointer2 = value; }
}
void uS_VideoInit() {
// set main configuration flags, display disabled
m74_config = 0;
m74_discol = 0x49;
SpiRamInit();
uS_ClearLowSpiRam();
// set rendering parameters for 4:3 display
SetRenderingParameters(24, 216);
// set row selector
m74_rows = (u16)(&rowsel[0]);
// set tile row VRAM addresses
m74_vaddr = (u16)(&vramrow_offs[0]);
// set up VRAM
for (u16 i = 0; i < HIGHEST_VRAM_ROW+1; i++) {
vramrow[i].config = 7; // row mode: 7
vramrow[i].img_addr = (i * 48 * 8); // address in SPI RAM
vramrow[i].fgcol = 0xB6;
vramrow[i].bgcol = 0x49;
}
framebuffer_addr = vramrow[0].img_addr;
// copy font data into SPI RAM
SpiRamSeqWriteStart(M74_M67_FONT_OFF / 65536, M74_M67_FONT_OFF % 65536);
for (u16 i = 0; i < 2048; i++) {
SpiRamSeqWriteU8(pgm_read_byte(&res_font[i]));
}
SpiRamSeqWriteEnd();
m74_config |= M74_CFG_ENABLE;
WaitVsync(8);
snesMouseEnabled = true;
SetUserPostVsyncCallback(uS_ReadMouse);
WaitVsync(20);
uS_EndFrameDraw();
}
void uS_Die(char *string) {
uS_HideCursor();
uS_BlitStrRam((char *)string, 0, 0);
while (true);
}
// call this at the beginning of your frame loop
void uS_BeginFrameDraw() {
uS_HideCursor();
//if ((DetectControllers() & 2) == 2) {
// mouse_detected = true;
// uS_UpdateMouse();
//} else {
// mouse_detected = false;
//}
uS_UpdateMouse();
uS_UpdateKeyboard();
}
// call this at the end of your frame loop
void uS_EndFrameDraw() {
uS_ShowCursor();
// DEBUG: remove this later!
//if (IsPowerSwitchPressed()) asm ("jmp 0x0000");
WaitVsync(1);
}
// wait for a new frame, while also keeping the mouse and keyboard polled
void uS_WaitFrame() {
uS_EndFrameDraw();
uS_BeginFrameDraw();
}
void uS_ClearLowSpiRam() {
// disable the screen while accessing SPI RAM
// this is to prevent data corruption that will occur if the
// SPI RAM is accessed right at the beginning of a frame
u8 old_config = m74_config;
m74_config = 0;
SpiRamSeqWriteStart(0, 0);
for (u16 i = 0; i != 0xFFFF; i++) {
SpiRamSeqWriteU8(0);
}
SpiRamSeqWriteEnd();
// restore the old screen enable state
m74_config = old_config;
}
// blit an 8x8 bitmap to the screen
// `mask` can be zero to draw the entire bitmap without transparency
void uS_Blit(u8 *bitmap, u8 *mask, u16 x, u16 y) {
u8 default_mask[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
if (mask == 0) mask = &default_mask[0];
// thanks to my friend mebibytedraco for helping with this!
u16 byte_off = (y * 384L + x) / 8L;
u8 bit_off = (y * 384 + x) % 8;
for (u8 line = 0; line < 8; line++) {
u8 high_byte = uS_XramReadU8(0, framebuffer_addr + byte_off);
u8 low_byte = uS_XramReadU8(0, framebuffer_addr + byte_off + 1);
u16 word = ((u16)high_byte << 8) | low_byte;
u16 mask_word = ((u16)mask[line] << 8) >> bit_off;
word &= ~mask_word;
u16 sprite = ((u16)bitmap[line] << 8) >> bit_off;
sprite |= word;
uS_XramWriteU8(0, framebuffer_addr + byte_off, sprite >> 8);
uS_XramWriteU8(0, framebuffer_addr + byte_off + 1, sprite & 0xFF);
byte_off += 384L / 8L;
}
}
// blit an 8x8 character bitmap to the screen
void uS_BlitChar(unsigned char character, u16 x, u16 y) {
u8 char_bitmap[8];
for (u8 i = 0; i < 8; i++)
char_bitmap[i] = pgm_read_byte(&res_font[character * 8 + i]);
uS_Blit(char_bitmap, 0, x, y);
}
void uS_BlitStr(const char *str, u16 x, u16 y) {
do {
uS_BlitChar(pgm_read_byte(str), x, y);
x += 8;
} while (pgm_read_byte(str++));
}
void uS_BlitStrRam(char *str, u16 x, u16 y) {
do {
uS_BlitChar(*str, x, y);
x += 8;
} while (*str++);
}