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 }
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
Definition: lcd.c:235
Definition: lcd.c:40