SayoriOS 0.3.3

lcd.c
1 #include "lcd.h"
2 #include "cpu.h"
3 #include "interrupt.h"
4 #include "sdl.h"
5 #include "mem.h"
6
7 #include "lib/string.h"
8
9 static int leftover;
10 static int lcd_cycles;
11 static int lcd_line, prev_line;
12 static int lcd_ly_compare;
13
14 /* LCD STAT */
15 static int ly_int; /* LYC = LY coincidence interrupt enable */
16 static int oam_int;
17 static int vblank_int;
18 static int hblank_int;
19 static int lcd_mode;
20
21 static int irq_level, new_level;
22
23 /* LCD Control */
24 static int lcd_enabled;
25 static int window_tilemap_select;
26 static int window_enabled;
27 static int tilemap_select;
28 static int bg_tiledata_select;
29 static int sprite_size;
30 static int sprites_enabled;
31 static int bg_enabled;
32 static int scroll_x, scroll_y;
33 static int window_x, window_y;
34
35 static int bgpalette[] = {0, 3, 3, 3};
36 static int sprpalette1[] = {0, 1, 2, 3};
37 static int sprpalette2[] = {0, 1, 2, 3};
38 static unsigned long colours[4] = {0xF4FFF4, 0xC0D0C0, 0x80A080, 0x001000};
39
40 struct sprite {
41 int y, x, tile, flags;
42 };
43
44 enum {
45 PRIO = 0x80,
46 VFLIP = 0x40,
47 HFLIP = 0x20,
48 PNUM = 0x10
49 };
50
51 enum LCD_INT {
52 LCD_HBLANK = 1,
53 LCD_VBLANK = 2,
54 LCD_OAM = 4,
55 LCD_LY = 8
56 };
57
58 void lcd_write_bg_palette(unsigned char n)
59 {
60 bgpalette[0] = (n>>0)&3;
61 bgpalette[1] = (n>>2)&3;
62 bgpalette[2] = (n>>4)&3;
63 bgpalette[3] = (n>>6)&3;
64 }
65
66 void lcd_write_spr_palette1(unsigned char n)
67 {
68 sprpalette1[0] = 0;
69 sprpalette1[1] = (n>>2)&3;
70 sprpalette1[2] = (n>>4)&3;
71 sprpalette1[3] = (n>>6)&3;
72 }
73
74 void lcd_write_spr_palette2(unsigned char n)
75 {
76 sprpalette2[0] = 0;
77 sprpalette2[1] = (n>>2)&3;
78 sprpalette2[2] = (n>>4)&3;
79 sprpalette2[3] = (n>>6)&3;
80 }
81
82 void lcd_write_scroll_x(unsigned char n)
83 {
84 scroll_x = n;
85 }
86
87 void lcd_write_scroll_y(unsigned char n)
88 {
89 scroll_y = n;
90 }
91
92 int lcd_get_line(void)
93 {
94 #ifdef DEBUG
95 return 0x90;
96 #else
97 if(lcd_line == 153 && (leftover % 456) >= 4)
98 return 0;
99 else
100 return lcd_line;
101 #endif
102 }
103
104 unsigned char lcd_get_stat(void)
105 {
106 unsigned char coincidence = (lcd_line == lcd_ly_compare) << 2;
107 return 0x80 | ly_int | oam_int | vblank_int | hblank_int | coincidence | lcd_mode;
108 }
109
110 void lcd_write_stat(unsigned char c)
111 {
112 ly_int = c&0x40;
113 oam_int = c&0x20;
114 vblank_int = c&0x10;
115 hblank_int = c&0x08;
116 }
117
118 static unsigned int *b;
119
120 void lcd_write_control(unsigned char c)
121 {
122 /* LCD just got turned on */
123 if(!lcd_enabled && (c & 0x80))
124 lcd_cycles = 0;
125
126 bg_enabled = !!(c & 0x01);
127 sprites_enabled = !!(c & 0x02);
128 sprite_size = !!(c & 0x04);
129 tilemap_select = !!(c & 0x08);
130 bg_tiledata_select = !!(c & 0x10);
131 window_enabled = !!(c & 0x20);
132 window_tilemap_select = !!(c & 0x40);
133 lcd_enabled = !!(c & 0x80);
134 }
135
136 unsigned char lcd_get_ly_compare(void)
137 {
138 return lcd_ly_compare;
139 }
140
141 void lcd_set_ly_compare(unsigned char c)
142 {
143 lcd_ly_compare = c;
144 }
145
146 void lcd_set_window_y(unsigned char n) {
147 window_y = n;
148 }
149
150 void lcd_set_window_x(unsigned char n) {
151 window_x = n;
152 }
153
154 static void POKE(unsigned int x, int y, int c)
155 {
156 b[(y*4+0)*640 + x*4+0] = c;
157 b[(y*4+0)*640 + x*4+1] = c;
158 b[(y*4+0)*640 + x*4+2] = c;
159 b[(y*4+0)*640 + x*4+3] = c;
160
161 b[(y*4+1)*640 + x*4+0] = c;
162 b[(y*4+1)*640 + x*4+1] = c;
163 b[(y*4+1)*640 + x*4+2] = c;
164 b[(y*4+1)*640 + x*4+3] = c;
165
166 b[(y*4+2)*640 + x*4+0] = c;
167 b[(y*4+2)*640 + x*4+1] = c;
168 b[(y*4+2)*640 + x*4+2] = c;
169 b[(y*4+2)*640 + x*4+3] = c;
170
171 b[(y*4+3)*640 + x*4+0] = c;
172 b[(y*4+3)*640 + x*4+1] = c;
173 b[(y*4+3)*640 + x*4+2] = c;
174 b[(y*4+3)*640 + x*4+3] = c;
175 }
176
177 static void swap(struct sprite *a, struct sprite *b)
178 {
179 struct sprite c;
180
181 c = *a;
182 *a = *b;
183 *b = c;
184 }
185
186 static void sort_sprites(struct sprite *s, int n)
187 {
188 int swapped, i;
189
190 do
191 {
192 swapped = 0;
193 for(i = 0; i < n-1; i++)
194 {
195 if(s[i].x > s[i+1].x)
196 {
197 swap(&s[i], &s[i+1]);
198 swapped = 1;
199 }
200 }
201 }
202 while(swapped);
203 }
204
205 static int sprites_update(int line, struct sprite *spr)
206 {
207 int i, c = 0;
208
209 for(i = 0; i < 40; i++)
210 {
211 int y;
212
213 y = mem_get_raw(0xFE00 + (i*4) + 0) - 16;
214
215 if(line < y || line >= y + 8 + (sprite_size*8))
216 continue;
217
218 spr[c].y = y;
219 spr[c].x = mem_get_raw(0xFE00 + (i*4) + 1) - 8;
220 spr[c].tile = mem_get_raw(0xFE00 + (i*4) + 2);
221 spr[c].flags = mem_get_raw(0xFE00 + (i*4) + 3);
222 c++;
223
224 if(c == 10)
225 break;
226 }
227
228 if(c)
229 sort_sprites(spr, c);
230
231 return c;
232 }
233
234 struct oam_cache
235 {
236 char colour;
237 char prio;
238 char pal;
239 };
240
241 static void sprite_fetch(int line, struct oam_cache *o)
242 {
243 struct sprite spr[10];
244 int i, x, sprite_count;
245
246 /* Fetch up to 10 in-range sprites for this scanline */
247 sprite_count = sprites_update(line, spr);
248
249 memset(o, 0, sizeof (struct oam_cache[160]));
250
251 /* Copy sprite pixels to oam_cache */
252 for(i = 0; i < sprite_count; i++)
253 {
254 int sprite_line;
255 unsigned short tile_addr;
256 unsigned char b1, b2, mask;
257
258 /* Sprite is too far right to ever render anything */
259 if(spr[i].x >= 160)
260 continue;
261
262 if(spr[i].flags & VFLIP)
263 sprite_line = (sprite_size ? 15 : 7) - (line - spr[i].y);
264 else
265 sprite_line = line - spr[i].y;
266
267 if(sprite_size)
268 tile_addr = 0x8000 + (spr[i].tile & 0xFE) * 16 + sprite_line * 2;
269 else
270 tile_addr = 0x8000 + spr[i].tile * 16 + sprite_line * 2;
271
272 b1 = mem_get_raw(tile_addr);
273 b2 = mem_get_raw(tile_addr + 1);
274
275 for(x = spr[i].x; x < spr[i].x + 8; x++)
276 {
277 int relx, new_col;
278
279 /* Sprite pixel is off the left hand side of the screen */
280 if(x < 0)
281 continue;
282
283 /* This pixel is offscreen, we're done with this sprite */
284 if(x >= 160)
285 break;
286
287 relx = x - spr[i].x;
288
289 mask = spr[i].flags & HFLIP ? 128>>(7-relx) : 128>>relx;
290 new_col = (!!(b2&mask))<<1 | !!(b1&mask);
291
292 /* We've already drawn this pixel, don't overwrite */
293 if(o[x].colour)
294 continue;
295
296 /* Pixel was blank, put our pixel's details in */
297 o[x].colour = new_col;
298 o[x].prio = spr[i].flags & PRIO;
299 o[x].pal = spr[i].flags & PNUM;
300 }
301 }
302 }
303
304 /* Process scanline 'line', cycle 'cycle' within that line */
305 static void lcd_do_line(int line, int cycle)
306 {
307 static struct oam_cache o[160];
308 static int line_fill, fetch_delay, window_lines, window_used_line, window_used_frame;
309 static unsigned char scx_low_latch;
310
311 if(fetch_delay)
312 {
313 fetch_delay--;
314 return;
315 }
316
317 if(line >= 144)
318 {
319 lcd_mode = 1;
320 window_lines = 0;
321 window_used_frame = 0;
322 return;
323 }
324
325 if(lcd_mode != 2 && cycle < 80)
326 {
327 lcd_mode = 2;
328 }
329 else
330 if(lcd_mode == 2 && cycle >= 80)
331 {
332 scx_low_latch = scroll_x & 7;
333 sprite_fetch(line, o);
334 lcd_mode = 3;
335 }
336
337 if(cycle < 93)
338 return;
339
340 if(lcd_mode == 3)
341 {
342 struct oam_cache *oc;
343 int colour = 0, bgcol;
344 unsigned int map_select, map_offset, tile_num, tile_addr, xm, ym;
345 unsigned char b1, b2, mask;
346
347 if(line >= window_y && window_enabled && line - window_y < 144 && (window_x - 7) <= line_fill)
348 {
349 if(!window_used_frame && line == window_y)
350 window_used_frame = 1;
351
352 if(!window_used_frame)
353 goto bg;
354 xm = line_fill - (window_x-7);
355 ym = window_lines;
356 map_select = window_tilemap_select;
357 window_used_line = 1;
358 }
359 else
360 {
361 bg:
362 if(!bg_enabled)
363 {
364 bgcol = 0;
365 goto skip_bg;
366 }
367
368 xm = (line_fill + (scroll_x & 0xF8) + scx_low_latch)%256;
369 ym = (line + scroll_y)%256;
370 map_select = tilemap_select;
371 }
372
373 map_offset = (ym/8)*32 + xm/8;
374
375 tile_num = mem_get_raw(0x9800 + map_select*0x400 + map_offset);
376 if(bg_tiledata_select)
377 tile_addr = 0x8000 + tile_num*16;
378 else
379 tile_addr = 0x9000 + ((signed char)tile_num)*16;
380
381 b1 = mem_get_raw(tile_addr+(ym%8)*2);
382 b2 = mem_get_raw(tile_addr+(ym%8)*2+1);
383
384 mask = 128>>(xm%8);
385
386 bgcol = (!!(b2&mask)<<1) | !!(b1&mask);
387
388 skip_bg:
389 oc = &o[line_fill];
390
391 if(sprites_enabled && oc->colour && ((oc->prio && !bgcol) || (!oc->prio)))
392 {
393 int *pal = oc->pal ? sprpalette2 : sprpalette1;
394 colour = colours[pal[(int)oc->colour]];
395 }
396 else
397 {
398 colour = colours[bgpalette[bgcol]];
399 }
400
401 POKE(line_fill, line, colour);
402
403 if(line_fill++ == 159)
404 {
405 lcd_mode = 0;
406 line_fill = 0;
407 if(window_used_line)
408 window_lines++;
409 window_used_line = 0;
410 scx_low_latch = 0;
411 }
412 }
413 }
414
415 static void lcd_interrupt(enum LCD_INT src)
416 {
417 new_level |= src;
418
419 if(!!irq_level != !!new_level)
420 interrupt(INTR_LCDSTAT);
421 }
422
423 int lcd_cycle(void)
424 {
425 /* Amount of cycles left over since the last full frame */
426 leftover = lcd_cycles % (456 * 154);
427
428 /* Each scanline is 456 cycles */
429 lcd_line = leftover / 456;
430
431 if(lcd_enabled)
432 {
433 if(ly_int)
434 {
435 if(lcd_line == lcd_ly_compare || (lcd_line == 153 && lcd_ly_compare == 0 && (leftover % 456) >= 4))
436 lcd_interrupt(LCD_LY);
437 }
438 if(oam_int && lcd_mode == 2)
439 lcd_interrupt(LCD_OAM);
440 if(vblank_int && lcd_line >= 144)
441 lcd_interrupt(LCD_VBLANK);
442 if(hblank_int && lcd_mode == 0)
443 lcd_interrupt(LCD_HBLANK);
444
445 lcd_do_line(lcd_line, leftover % 456);
446 }
447
448 if(lcd_line == 144 && prev_line == 143)
449 {
450 if(sdl_update())
451 return 0;
452
453 sdl_frame();
454 if(lcd_enabled)
455 {
456 interrupt(INTR_VBLANK);
457 }
458 }
459
460 prev_line = lcd_line;
461
462 lcd_cycles++;
463
464 irq_level = new_level;
465 new_level = 0;
466
467 return 1;
468 }
469
470 int lcd_init(void)
471 {
472 int r;
473
474 r = sdl_init();
475 if(r)
476 return 1;
477
478 b = sdl_get_framebuffer();
479
480 #ifdef DEBUG
481 lcd_write_control(91);
482 #endif
483
484 return 0;
485 }
memset
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
oam_cache
Definition: lcd.c:235
sprite
Definition: lcd.c:40