retrograph 0.5-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/COPYING +19 -0
  2. data/README +264 -0
  3. data/Rakefile +71 -0
  4. data/ext/retrograph/extconf.rb +6 -0
  5. data/ext/retrograph/retrograph.c +324 -0
  6. data/lib/retrograph/sdl.rb +26 -0
  7. data/lib/retrograph.rb +29 -0
  8. data/lib/retrograph.so +0 -0
  9. data/spec/image-specs/color-blue-bits.case +2 -0
  10. data/spec/image-specs/color-blue-bits.png +0 -0
  11. data/spec/image-specs/color-green-bits.case +2 -0
  12. data/spec/image-specs/color-green-bits.png +0 -0
  13. data/spec/image-specs/color-red-bits.case +2 -0
  14. data/spec/image-specs/color-red-bits.png +0 -0
  15. data/spec/image-specs/default-screen-black.case +1 -0
  16. data/spec/image-specs/default-screen-black.png +0 -0
  17. data/spec/image-specs/default-screen-color-0.case +2 -0
  18. data/spec/image-specs/default-screen-color-0.png +0 -0
  19. data/spec/image-specs/scroll-mode1.case +7 -0
  20. data/spec/image-specs/scroll-mode1.png +0 -0
  21. data/spec/image-specs/scroll-mode2.case +10 -0
  22. data/spec/image-specs/scroll-mode2.png +0 -0
  23. data/spec/image-specs/scroll-mode3.case +10 -0
  24. data/spec/image-specs/scroll-mode3.png +0 -0
  25. data/spec/image-specs/sprites-doubling.case +5 -0
  26. data/spec/image-specs/sprites-doubling.png +0 -0
  27. data/spec/image-specs/sprites-mode2.case +8 -0
  28. data/spec/image-specs/sprites-mode2.png +0 -0
  29. data/spec/image-specs/sprites-mode3.case +8 -0
  30. data/spec/image-specs/sprites-mode3.png +0 -0
  31. data/spec/image-specs/sprites-ordering.case +5 -0
  32. data/spec/image-specs/sprites-ordering.png +0 -0
  33. data/spec/image-specs/text-colors.case +5 -0
  34. data/spec/image-specs/text-colors.png +0 -0
  35. data/spec/image-specs/tile-attributes-mode2.case +9 -0
  36. data/spec/image-specs/tile-attributes-mode2.png +0 -0
  37. data/spec/image-specs/tile-attributes-mode3.case +9 -0
  38. data/spec/image-specs/tile-attributes-mode3.png +0 -0
  39. data/spec/retrograph_spec.rb +134 -0
  40. data/src/retrograph.c +697 -0
  41. data/src/retrograph.h +67 -0
  42. metadata +99 -0
data/src/retrograph.c ADDED
@@ -0,0 +1,697 @@
1
+ /* retrograph
2
+ *
3
+ * Copyright (c) 2009 MenTaLguY <mental@rydia.net>
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in
13
+ * all copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ * THE SOFTWARE.
22
+ */
23
+ #include "retrograph.h"
24
+ #include <string.h>
25
+
26
+ #define MAX_SPRITES 64
27
+ #define MAX_SPRITES_PER_SCANLINE 8
28
+
29
+ typedef enum {
30
+ TEXT_A=0,
31
+ TEXT_B=1,
32
+ GRAPHICS_A=2,
33
+ GRAPHICS_B=3
34
+ } vdu_mode_t;
35
+
36
+ static inline int is_graphics_mode(vdu_mode_t mode) {
37
+ return mode >> 1;
38
+ }
39
+
40
+ static inline uint8_t SPRITE_ATTRS_TO_GENERIC(uint8_t attrs) {
41
+ return attrs << 1;
42
+ }
43
+ static inline uint8_t BG_ATTRS_TO_GENERIC(uint8_t attrs) {
44
+ return (attrs & 0x0f) | ((attrs << 1) & 0xe0);
45
+ }
46
+
47
+ #define ROW_BYTES (RETROGRAPH_DISPLAY_WIDTH * RETROGRAPH_BYTES_PER_PIXEL)
48
+ #define TEXT_PATTERN_STRIDE 8
49
+ #define GRAPHICS_A_PATTERN_STRIDE 16
50
+ #define GRAPHICS_B_PATTERN_STRIDE 32
51
+
52
+ #define TILE_VFLIP_BIT 13
53
+ #define TILE_VFLIP_MASK (1 << TILE_VFLIP_BIT)
54
+ #define TILE_HFLIP_BIT 14
55
+ #define TILE_HFLIP_MASK (1 << TILE_HFLIP_BIT)
56
+
57
+ #define PRIORITY_BIT 15
58
+ #define PRIORITY_MASK (1 << PRIORITY_BIT)
59
+
60
+ #define ENABLE_SPRITES_BIT 3
61
+ #define ENABLE_BG_BIT 2
62
+
63
+ #define TILE_COLOR_SHIFT 9
64
+ #define TILE_COLOR_MASK 0x0e00
65
+ #define TILE_COLOR_LOSS 2
66
+ #define TILE_COLOR_HI_BIT (TILE_COLOR_SHIFT+2)
67
+ #define TILE_COLOR_HI_MASK (1 << TILE_COLOR_HI_BIT)
68
+ #define TILE_PATTERN_SHIFT 0
69
+ #define TILE_PATTERN_MASK 0x1ff
70
+ #define TILE_PATTERN_HI_BIT 8
71
+ #define TILE_PATTERN_HI_MASK (1 << TILE_PATTERN_HI_BIT)
72
+ #define SPRITE_VDOUBLE_BIT 11
73
+ #define SPRITE_VDOUBLE_MASK (1 << SPRITE_VDOUBLE_BIT)
74
+ #define SPRITE_HDOUBLE_BIT 12
75
+ #define SPRITE_HDOUBLE_MASK (1 << SPRITE_HDOUBLE_BIT)
76
+
77
+ struct retrograph_vdu_tag {
78
+ retrograph_error_t error;
79
+ unsigned refcount;
80
+
81
+ uint8_t vram[16384];
82
+ uint8_t sprites[256];
83
+ uint8_t palette[32];
84
+ char enable_bg;
85
+ char enable_sprites;
86
+ vdu_mode_t mode;
87
+ retrograph_addr_t text_pattern_base;
88
+ retrograph_addr_t bg_pattern_base;
89
+ retrograph_addr_t name_base;
90
+ uint8_t bg_scroll_x;
91
+ uint8_t bg_scroll_y;
92
+
93
+ unsigned current_scanline;
94
+ retrograph_pixel_t palette_cache[32];
95
+ /* +16 to accomodate double-width sprites */
96
+ retrograph_pixel_t row_buffer[RETROGRAPH_DISPLAY_WIDTH + 16];
97
+ };
98
+
99
+ static retrograph_error_t no_memory_error = RETROGRAPH_NO_MEMORY;
100
+ #define NO_MEMORY_SINGLETON ((retrograph_vdu_t)&no_memory_error);
101
+ static inline int is_singleton_vdu(struct retrograph_vdu_tag const *vdu) {
102
+ return vdu == NO_MEMORY_SINGLETON;
103
+ }
104
+
105
+ static inline retrograph_addr_t decode_text_pattern_base(uint8_t base) {
106
+ return (base & 0x30) << 8;
107
+ }
108
+
109
+ static inline retrograph_addr_t decode_bg_pattern_base(uint8_t base) {
110
+ return (base & 0x20) << 8;
111
+ }
112
+
113
+ static inline retrograph_addr_t decode_name_base(uint8_t base) {
114
+ return ((base & 0x30) | 0x08) << 8;
115
+ }
116
+
117
+ static inline retrograph_addr_t compute_sprite_pattern_base(
118
+ retrograph_addr_t bg_pattern_base
119
+ ) {
120
+ return 0x2000 - bg_pattern_base;
121
+ }
122
+
123
+ retrograph_vdu_t retrograph_new() {
124
+ retrograph_vdu_t vdu = (retrograph_vdu_t)malloc(sizeof(struct retrograph_vdu_tag));
125
+ if (vdu) {
126
+ vdu->error = RETROGRAPH_OK;
127
+ vdu->refcount = 1;
128
+ memset(&vdu->vram, 0, sizeof(vdu->vram));
129
+ memset(&vdu->sprites, 0, sizeof(vdu->sprites));
130
+ memset(&vdu->palette, 0, sizeof(vdu->palette));
131
+ vdu->enable_bg = 0;
132
+ vdu->enable_sprites = 0;
133
+ vdu->mode = TEXT_A;
134
+ vdu->text_pattern_base = decode_text_pattern_base(0);
135
+ vdu->bg_pattern_base = decode_bg_pattern_base(0);
136
+ vdu->name_base = decode_name_base(0);
137
+ vdu->bg_scroll_x = 0;
138
+ vdu->bg_scroll_y = 0;
139
+ vdu->current_scanline = RETROGRAPH_DISPLAY_HEIGHT;
140
+ } else {
141
+ vdu = NO_MEMORY_SINGLETON;
142
+ }
143
+ return vdu;
144
+ }
145
+
146
+ void retrograph_ref(retrograph_vdu_t vdu) {
147
+ if (vdu && !is_singleton_vdu(vdu)) {
148
+ vdu->refcount++;
149
+ }
150
+ }
151
+
152
+ void retrograph_destroy(retrograph_vdu_t vdu) {
153
+ if (vdu && !is_singleton_vdu(vdu) && !--vdu->refcount) {
154
+ free(vdu);
155
+ }
156
+ }
157
+
158
+ retrograph_error_t retrograph_get_error(retrograph_vdu_t vdu) {
159
+ if (vdu) {
160
+ retrograph_error_t const error = vdu->error;
161
+ if (!is_singleton_vdu(vdu)) {
162
+ vdu->error = RETROGRAPH_OK;
163
+ }
164
+ return error;
165
+ } else {
166
+ return RETROGRAPH_INVALID_ARGUMENT;
167
+ }
168
+ }
169
+
170
+ static void write_vram(retrograph_vdu_t vdu, retrograph_addr_t offset, uint8_t const *bytes, retrograph_addr_t n_bytes) {
171
+ memcpy(vdu->vram + offset, bytes, n_bytes);
172
+ }
173
+
174
+ static void write_sprites(retrograph_vdu_t vdu, retrograph_addr_t offset, uint8_t const *bytes, retrograph_addr_t n_bytes) {
175
+ memcpy(vdu->sprites + offset, bytes, n_bytes);
176
+ }
177
+
178
+ static void write_palette(retrograph_vdu_t vdu, retrograph_addr_t offset, uint8_t const *bytes, retrograph_addr_t n_bytes) {
179
+ memcpy(vdu->palette + offset, bytes, n_bytes);
180
+ }
181
+
182
+ static void write_unmapped(retrograph_vdu_t vdu, retrograph_addr_t offset, uint8_t const *bytes, retrograph_addr_t n_bytes) {
183
+ }
184
+
185
+ static void write_registers(retrograph_vdu_t vdu, retrograph_addr_t offset, uint8_t const *bytes, retrograph_addr_t n_bytes) {
186
+ for (; n_bytes > 0; n_bytes--, offset++, bytes++) {
187
+ switch (offset) {
188
+ case 0:
189
+ vdu->enable_sprites = (*bytes >> ENABLE_SPRITES_BIT) & 1;
190
+ vdu->enable_bg = (*bytes >> ENABLE_BG_BIT) & 1;
191
+ vdu->mode = (vdu_mode_t)(*bytes & 3);
192
+ break;
193
+ case 1:
194
+ vdu->text_pattern_base = decode_text_pattern_base(*bytes);
195
+ vdu->bg_pattern_base = decode_bg_pattern_base(*bytes);
196
+ break;
197
+ case 2:
198
+ vdu->name_base = decode_name_base(*bytes);
199
+ break;
200
+ case 4:
201
+ vdu->bg_scroll_x = *bytes;
202
+ break;
203
+ case 5:
204
+ vdu->bg_scroll_y = *bytes;
205
+ break;
206
+ }
207
+ }
208
+ }
209
+
210
+ typedef void (*write_func_t)(retrograph_vdu_t vdu, retrograph_addr_t offset, uint8_t const *bytes, retrograph_addr_t n_bytes);
211
+
212
+ typedef struct mem_region_tag {
213
+ retrograph_addr_t start_address;
214
+ write_func_t write_func;
215
+ } mem_region_t;
216
+
217
+ static mem_region_t const mem_regions[] = {
218
+ { 0x0000, write_vram },
219
+ { 0x4000, write_vram },
220
+ { 0x7e00, write_sprites },
221
+ { 0x7f00, write_palette },
222
+ { 0x7f20, write_unmapped },
223
+ { 0x7ff8, write_registers },
224
+ { 0x8000, NULL }
225
+ };
226
+
227
+ void retrograph_write(retrograph_vdu_t vdu, retrograph_addr_t start_address,
228
+ void const *data, size_t n_bytes)
229
+ {
230
+ size_t start;
231
+ uint8_t const *bytes = (uint8_t const *)data;
232
+
233
+ if (!vdu || vdu->error) {
234
+ return;
235
+ }
236
+
237
+ if (n_bytes < 0) {
238
+ vdu->error = RETROGRAPH_INVALID_ARGUMENT;
239
+ return;
240
+ }
241
+
242
+ start = start_address;
243
+ while (n_bytes > 0) {
244
+ mem_region_t const *region = mem_regions;
245
+ size_t end;
246
+ size_t length;
247
+ start &= 0x7fff;
248
+ while (start >= (size_t)(region+1)->start_address) {
249
+ region++;
250
+ }
251
+ end = start + n_bytes;
252
+ if (end >= (size_t)(region+1)->start_address) {
253
+ end = (region+1)->start_address;
254
+ }
255
+ length = end - start;
256
+ region->write_func(vdu,
257
+ (retrograph_addr_t)start - region->start_address,
258
+ bytes, (retrograph_addr_t)length);
259
+ n_bytes -= length;
260
+ bytes += length;
261
+ start += length;
262
+ }
263
+ }
264
+
265
+ static void update_palette_cache(retrograph_vdu_t vdu) {
266
+ int i;
267
+ for (i = 0; i < 32; i++) {
268
+ uint8_t entry;
269
+ uint8_t r, g, b;
270
+ entry = vdu->palette[i];
271
+ r = (entry & 0x30) << 2;
272
+ r |= r >> 2;
273
+ r |= r >> 4;
274
+ g = (entry & 0x0c) << 4;
275
+ g |= g >> 2;
276
+ g |= g >> 4;
277
+ b = (entry & 0x03) << 6;
278
+ b |= b >> 2;
279
+ b |= b >> 4;
280
+ vdu->palette_cache[i] = (r << RETROGRAPH_RED_SHIFT) |
281
+ (g << RETROGRAPH_GREEN_SHIFT) |
282
+ (b << RETROGRAPH_BLUE_SHIFT);
283
+ }
284
+ }
285
+
286
+ static void clear_row_buffer(retrograph_pixel_t * const out_buffer,
287
+ retrograph_pixel_t const color)
288
+ {
289
+ int i;
290
+ for (i = 0; i < RETROGRAPH_DISPLAY_WIDTH; i++) {
291
+ out_buffer[i] = color;
292
+ }
293
+ }
294
+
295
+ #define DEFINE_RENDER_CHAR(width) \
296
+ static inline void render_char ## width(retrograph_pixel_t * const out_buffer, \
297
+ uint8_t const c, \
298
+ uint8_t const attrs, \
299
+ uint8_t const * const patterns, \
300
+ retrograph_pixel_t const * const palette) \
301
+ { \
302
+ uint8_t pattern_row; \
303
+ retrograph_pixel_t colors[2]; \
304
+ int i; \
305
+ colors[1] = palette[attrs % 16]; \
306
+ colors[0] = palette[attrs / 16]; \
307
+ pattern_row = patterns[c * TEXT_PATTERN_STRIDE]; \
308
+ for (i = 0; i < width; i++) { \
309
+ out_buffer[i] = colors[pattern_row & 1]; \
310
+ pattern_row >>= 1; \
311
+ } \
312
+ }
313
+
314
+ DEFINE_RENDER_CHAR(6)
315
+ DEFINE_RENDER_CHAR(8)
316
+
317
+ static void render_line_text_a(retrograph_pixel_t *out_buffer, uint8_t const *names,
318
+ uint8_t y_fine, uint8_t const *patterns,
319
+ retrograph_pixel_t const *palette, char enable_bg)
320
+ {
321
+ int i;
322
+
323
+ if (enable_bg) {
324
+ patterns += y_fine;
325
+
326
+ /* 8px padding on either side of display */
327
+ out_buffer += 8;
328
+
329
+ /* draw text columns */
330
+ for (i = 0; i < 40; i++) {
331
+ render_char6(out_buffer, names[0], names[1], patterns, palette);
332
+ out_buffer += 6;
333
+ names += 2;
334
+ }
335
+ }
336
+ }
337
+
338
+ static void render_line_text_b(retrograph_pixel_t *out_buffer, uint8_t const *names,
339
+ uint8_t y_fine, uint8_t const *patterns,
340
+ retrograph_pixel_t const *palette, char enable_bg)
341
+ {
342
+ int i;
343
+ if (enable_bg) {
344
+ patterns += y_fine;
345
+ for (i = 0; i < 32; i++) {
346
+ render_char8(out_buffer, names[0], names[1], patterns, palette);
347
+ out_buffer += 8;
348
+ names += 2;
349
+ }
350
+ }
351
+ }
352
+
353
+ static inline uint16_t unpack_uint16(uint8_t const * const bytes) {
354
+ return (uint16_t)((bytes[1] << 8) | bytes[0]);
355
+ }
356
+
357
+ static inline uint32_t unpack_uint32(uint8_t const * const bytes) {
358
+ return (uint32_t)((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]);
359
+ }
360
+
361
+ #define RENDER_SINGLE_PIXEL out_buffer[i] = palette[color | base_color]
362
+ #define RENDER_DOUBLE_PIXEL out_buffer[i*2] = palette[color | base_color]; \
363
+ out_buffer[i*2+1] = palette[color | base_color]
364
+
365
+ #define DEFINE_RENDER_TILE(bpp, suffix, RENDER_PIXEL, row_type, unpack_row) \
366
+ static inline void render_ ## suffix ## bpp(retrograph_pixel_t *out_buffer, \
367
+ uint16_t cell, \
368
+ uint8_t const *vflip_patterns[2], \
369
+ retrograph_pixel_t const *palette) \
370
+ { \
371
+ row_type pattern_row; \
372
+ uint8_t base_color; \
373
+ int i; \
374
+ { \
375
+ uint8_t const *pattern_ptr; \
376
+ pattern_ptr = vflip_patterns[(cell >> TILE_VFLIP_BIT) & 1] + \
377
+ ((cell & TILE_PATTERN_MASK) >> TILE_PATTERN_SHIFT) * \
378
+ bpp * 8; \
379
+ pattern_row = unpack_row(pattern_ptr); \
380
+ } \
381
+ base_color = (uint8_t)((cell & TILE_COLOR_MASK) >> \
382
+ (TILE_COLOR_SHIFT - TILE_COLOR_LOSS)); \
383
+ if (cell & TILE_HFLIP_MASK) { \
384
+ for (i = 0; i < 8; i++) { \
385
+ uint8_t color; \
386
+ color = (uint8_t)(pattern_row >> ((8 * bpp) - bpp)); \
387
+ if (color) { \
388
+ RENDER_PIXEL; \
389
+ } \
390
+ pattern_row <<= bpp; \
391
+ } \
392
+ } else { /* no hflip */ \
393
+ for (i = 0; i < 8; i++) { \
394
+ uint8_t color; \
395
+ color = (uint8_t)(pattern_row & ((1 << bpp) - 1)); \
396
+ if (color) { \
397
+ RENDER_PIXEL; \
398
+ } \
399
+ pattern_row >>= bpp; \
400
+ } \
401
+ } \
402
+ }
403
+
404
+ DEFINE_RENDER_TILE(2, tile, RENDER_SINGLE_PIXEL, uint16_t, unpack_uint16)
405
+ DEFINE_RENDER_TILE(2, tile_double, RENDER_DOUBLE_PIXEL, uint16_t, unpack_uint16)
406
+ DEFINE_RENDER_TILE(4, tile, RENDER_SINGLE_PIXEL, uint32_t, unpack_uint32)
407
+ DEFINE_RENDER_TILE(4, tile_double, RENDER_DOUBLE_PIXEL, uint32_t, unpack_uint32)
408
+
409
+ #define DEFINE_RENDER_BG_LINE(bpp) \
410
+ static void render_bg_line ## bpp(retrograph_pixel_t *out_buffer, \
411
+ uint8_t x_coarse, uint8_t x_fine, \
412
+ uint8_t const *names, \
413
+ uint8_t const *vflip_patterns[2], \
414
+ uint16_t toggle_bits, \
415
+ retrograph_pixel_t const *palette) \
416
+ { \
417
+ int i; \
418
+ uint8_t const *name_cursor; \
419
+ out_buffer += (8 - x_fine) % 8; \
420
+ x_coarse = (x_coarse + (x_fine != 0)) % 32; \
421
+ name_cursor = names + x_coarse * 2; \
422
+ /* render up to the end of the nametable row */ \
423
+ for (i = x_coarse; i < 32; i++) { \
424
+ uint16_t cell; \
425
+ cell = (uint16_t)name_cursor[0] | \
426
+ ((uint16_t)BG_ATTRS_TO_GENERIC(name_cursor[1]) << 8); \
427
+ cell ^= toggle_bits; \
428
+ if (!(cell & PRIORITY_MASK)) { \
429
+ render_tile ## bpp(out_buffer, cell, vflip_patterns, palette); \
430
+ } \
431
+ out_buffer += 8; \
432
+ name_cursor += 2; \
433
+ } \
434
+ /* render remainder of line from beginning of nametable row */ \
435
+ name_cursor = names; \
436
+ for (i = 0; i < x_coarse; i++) { \
437
+ uint16_t cell; \
438
+ cell = unpack_uint16(name_cursor) ^ toggle_bits; \
439
+ if (!(cell & PRIORITY_MASK)) { \
440
+ render_tile ## bpp(out_buffer, cell, vflip_patterns, palette); \
441
+ } \
442
+ out_buffer += 8; \
443
+ name_cursor += 2; \
444
+ } \
445
+ }
446
+
447
+ DEFINE_RENDER_BG_LINE(2)
448
+ DEFINE_RENDER_BG_LINE(4)
449
+
450
+ /* result is in sprite rows rather than display rows */
451
+ static inline unsigned compute_sprite_line(uint8_t row, uint8_t const *sprite) {
452
+ unsigned sprite_line;
453
+ /* sprite_y = sprite[1] - 1 */
454
+ sprite_line = row - (unsigned)sprite[1] + 1;
455
+ /* vertical doubling */
456
+ sprite_line >>= (SPRITE_ATTRS_TO_GENERIC(sprite[3]) &
457
+ (SPRITE_VDOUBLE_MASK >> 8)) >>
458
+ (SPRITE_VDOUBLE_BIT - 8);
459
+ return sprite_line;
460
+ }
461
+
462
+ static void render_sprites_line(retrograph_pixel_t *out_buffer, uint8_t row,
463
+ uint8_t const *sprites,
464
+ uint8_t const *patterns,
465
+ retrograph_pixel_t const *palette)
466
+ {
467
+ unsigned sprite_lines[MAX_SPRITES];
468
+ int i, n_sprites;
469
+ /* find the first MAX_SPRITES_PER_SCANLINE sprites spanning this line */
470
+ for (i = 0, n_sprites = 0;
471
+ i < MAX_SPRITES && n_sprites < MAX_SPRITES_PER_SCANLINE;
472
+ i++)
473
+ {
474
+ unsigned sprite_line;
475
+ /* takes double-height sprites into account */
476
+ sprite_line = compute_sprite_line(row, &sprites[i*4]);
477
+ /* row >= sprite_y && row < sprite_y + sprite_height */
478
+ if (sprite_line < 8) {
479
+ n_sprites++;
480
+ }
481
+ sprite_lines[i] = sprite_line;
482
+ }
483
+ /* work backwards, rendering sprites on this line as we encounter them */
484
+ for (i--; i >= 0; i--) {
485
+ uint8_t const * const sprite = &sprites[i*4];
486
+ unsigned sprite_line;
487
+ sprite_line = sprite_lines[i];
488
+ if (sprite_line < 8) {
489
+ uint16_t cell;
490
+ uint8_t sprite_x;
491
+ uint8_t const *vflip_patterns[2];
492
+ uint16_t render_cell;
493
+ cell = (uint16_t)sprite[2] |
494
+ ((uint16_t)SPRITE_ATTRS_TO_GENERIC(sprite[3]) << 8);
495
+ vflip_patterns[0] = patterns + 4 * sprite_line;
496
+ vflip_patterns[1] = patterns + 4 * (7 - sprite_line);
497
+ sprite_x = sprite[0];
498
+ render_cell = cell;
499
+ render_cell &= TILE_HFLIP_MASK | TILE_VFLIP_MASK |
500
+ (TILE_COLOR_MASK ^ TILE_COLOR_HI_MASK) |
501
+ (TILE_PATTERN_MASK ^ TILE_PATTERN_HI_MASK);
502
+ render_cell |= TILE_COLOR_HI_MASK;
503
+ if (cell & SPRITE_HDOUBLE_MASK) {
504
+ render_tile_double4(&out_buffer[sprite_x], render_cell,
505
+ vflip_patterns, palette);
506
+ } else {
507
+ render_tile4(&out_buffer[sprite_x], render_cell,
508
+ vflip_patterns, palette);
509
+ }
510
+ }
511
+ }
512
+ }
513
+
514
+ static void render_line_graphics_a(retrograph_pixel_t *out_buffer,
515
+ uint8_t const * const names,
516
+ uint8_t x_coarse, uint8_t x_fine,
517
+ uint8_t y_fine, uint8_t row,
518
+ uint8_t const *patterns,
519
+ retrograph_addr_t pattern_base,
520
+ uint8_t const *sprites,
521
+ retrograph_pixel_t const *palette,
522
+ char enable_bg, char enable_sprites)
523
+ {
524
+ uint8_t const *vflip_patterns[2];
525
+
526
+ vflip_patterns[0] = patterns + pattern_base + y_fine * 2;
527
+ vflip_patterns[1] = patterns + pattern_base + (7 - y_fine) * 2;
528
+
529
+ if (enable_bg) {
530
+ render_bg_line2(out_buffer, x_coarse, x_fine, names,
531
+ vflip_patterns, 0, palette);
532
+ }
533
+ if (enable_sprites) {
534
+ uint8_t const *sprite_patterns;
535
+ sprite_patterns = patterns + compute_sprite_pattern_base(pattern_base);
536
+ render_sprites_line(out_buffer, row, sprites, sprite_patterns,
537
+ palette);
538
+ }
539
+ if (enable_bg) {
540
+ render_bg_line2(out_buffer, x_coarse, x_fine, names,
541
+ vflip_patterns, PRIORITY_MASK, palette);
542
+ }
543
+ }
544
+
545
+ static void render_line_graphics_b(retrograph_pixel_t *out_buffer, uint8_t const *names,
546
+ uint8_t x_coarse, uint8_t x_fine,
547
+ uint8_t y_fine, uint8_t row,
548
+ uint8_t const *patterns,
549
+ retrograph_addr_t pattern_base,
550
+ uint8_t const *sprites,
551
+ retrograph_pixel_t const *palette,
552
+ char enable_bg, char enable_sprites)
553
+ {
554
+ uint8_t const *vflip_patterns[2];
555
+ uint16_t hi_index;
556
+
557
+ hi_index = (pattern_base != 0) << 9;
558
+ vflip_patterns[0] = patterns + y_fine * 4;
559
+ vflip_patterns[1] = patterns + (7 - y_fine) * 4;
560
+
561
+ if (enable_bg) {
562
+ render_bg_line4(out_buffer, x_coarse, x_fine, names,
563
+ vflip_patterns, hi_index, palette);
564
+ }
565
+ if (enable_sprites) {
566
+ uint8_t const *sprite_patterns;
567
+ sprite_patterns = patterns + compute_sprite_pattern_base(pattern_base);
568
+ render_sprites_line(out_buffer, row, sprites, sprite_patterns,
569
+ palette);
570
+ }
571
+ if (enable_bg) {
572
+ render_bg_line4(out_buffer, x_coarse, x_fine, names,
573
+ vflip_patterns, PRIORITY_MASK | hi_index, palette);
574
+ }
575
+ }
576
+
577
+ static void render_line(retrograph_vdu_t vdu, unsigned scanline, uint8_t *dest)
578
+ {
579
+ uint8_t const *name_row;
580
+ uint8_t bg_x_coarse;
581
+ uint8_t bg_x_fine;
582
+ uint8_t bg_y_coarse;
583
+ uint8_t bg_y_fine;
584
+
585
+ { /* scrolling */
586
+ unsigned bg_y;
587
+
588
+ if (is_graphics_mode(vdu->mode)) {
589
+ bg_y = scanline + vdu->bg_scroll_y;
590
+ } else {
591
+ /* text modes scroll vertically in full-character increments */
592
+ bg_y = scanline + (vdu->bg_scroll_y & ~7);
593
+ if (vdu->mode == TEXT_A) {
594
+ bg_y -= 12;
595
+ }
596
+ }
597
+
598
+ if (is_graphics_mode(vdu->mode)) {
599
+ bg_x_coarse = vdu->bg_scroll_x / 8;
600
+ bg_x_fine = vdu->bg_scroll_x % 8;
601
+ } else {
602
+ /* text modes have no horizontal scrolling */
603
+ bg_x_coarse = 0;
604
+ bg_x_fine = 0;
605
+ }
606
+
607
+ bg_y_coarse = (uint8_t)(bg_y / 8);
608
+ switch (vdu->mode) {
609
+ case TEXT_A:
610
+ bg_y_coarse %= 25;
611
+ break;
612
+ case TEXT_B:
613
+ bg_y_coarse %= 28;
614
+ break;
615
+ default:
616
+ bg_y_coarse %= 32;
617
+ }
618
+ bg_y_fine = (uint8_t)(bg_y % 8);
619
+ }
620
+
621
+ update_palette_cache(vdu);
622
+
623
+ switch (vdu->mode) {
624
+ case TEXT_A:
625
+ /* stride is 40 2-byte cells */
626
+ name_row = vdu->vram + vdu->name_base + bg_y_coarse * (40 * 2);
627
+ break;
628
+ default:
629
+ /* stride is 32 2-byte cells */
630
+ name_row = vdu->vram + vdu->name_base + bg_y_coarse * (32 * 2);
631
+ }
632
+
633
+ clear_row_buffer(vdu->row_buffer, vdu->palette_cache[0]);
634
+ switch (vdu->mode) {
635
+ case TEXT_A:
636
+ /* 12px padding above and below display */
637
+ if (scanline >= 12 ||
638
+ scanline < RETROGRAPH_DISPLAY_HEIGHT - 12)
639
+ {
640
+ render_line_text_a(vdu->row_buffer, name_row, bg_y_fine,
641
+ vdu->vram + vdu->text_pattern_base,
642
+ vdu->palette_cache, vdu->enable_bg);
643
+ }
644
+ break;
645
+ case TEXT_B:
646
+ render_line_text_b(vdu->row_buffer, name_row, bg_y_fine,
647
+ vdu->vram + vdu->text_pattern_base,
648
+ vdu->palette_cache, vdu->enable_bg);
649
+ break;
650
+ case GRAPHICS_A:
651
+ render_line_graphics_a(vdu->row_buffer, name_row,
652
+ bg_x_coarse, bg_x_fine, bg_y_fine,
653
+ scanline, vdu->vram,
654
+ vdu->bg_pattern_base,
655
+ vdu->sprites,
656
+ vdu->palette_cache,
657
+ vdu->enable_bg, vdu->enable_sprites);
658
+ break;
659
+ case GRAPHICS_B:
660
+ render_line_graphics_b(vdu->row_buffer, name_row,
661
+ bg_x_coarse, bg_x_fine, bg_y_fine,
662
+ scanline, vdu->vram,
663
+ vdu->bg_pattern_base,
664
+ vdu->sprites,
665
+ vdu->palette_cache,
666
+ vdu->enable_bg, vdu->enable_sprites);
667
+ break;
668
+ }
669
+ memcpy(dest, vdu->row_buffer, ROW_BYTES);
670
+ }
671
+
672
+ void retrograph_begin_frame(retrograph_vdu_t vdu) {
673
+ if (!vdu || vdu->error) {
674
+ return;
675
+ }
676
+ vdu->current_scanline = 0;
677
+ }
678
+
679
+ void retrograph_render_scanline(retrograph_vdu_t vdu, void *dest,
680
+ size_t dest_size)
681
+ {
682
+ if (!vdu || vdu->error) {
683
+ return;
684
+ }
685
+ if (dest_size < ROW_BYTES) {
686
+ return;
687
+ }
688
+
689
+ if (vdu->current_scanline < RETROGRAPH_DISPLAY_HEIGHT) {
690
+ render_line(vdu, vdu->current_scanline, (uint8_t *)dest);
691
+ vdu->current_scanline++;
692
+ } else {
693
+ update_palette_cache(vdu);
694
+ clear_row_buffer(vdu->row_buffer, vdu->palette_cache[0]);
695
+ memcpy(dest, vdu->row_buffer, ROW_BYTES);
696
+ }
697
+ }