retrograph 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README +248 -0
  2. data/Rakefile +54 -0
  3. data/ext/extconf.rb +3 -0
  4. data/ext/retrograph.c +275 -0
  5. data/spec/image-specs/color-blue-bits.case +2 -0
  6. data/spec/image-specs/color-blue-bits.png +0 -0
  7. data/spec/image-specs/color-green-bits.case +2 -0
  8. data/spec/image-specs/color-green-bits.png +0 -0
  9. data/spec/image-specs/color-red-bits.case +2 -0
  10. data/spec/image-specs/color-red-bits.png +0 -0
  11. data/spec/image-specs/default-screen-black.case +1 -0
  12. data/spec/image-specs/default-screen-black.png +0 -0
  13. data/spec/image-specs/default-screen-color-0.case +2 -0
  14. data/spec/image-specs/default-screen-color-0.png +0 -0
  15. data/spec/image-specs/scroll-mode1.case +7 -0
  16. data/spec/image-specs/scroll-mode1.png +0 -0
  17. data/spec/image-specs/scroll-mode2.case +10 -0
  18. data/spec/image-specs/scroll-mode2.png +0 -0
  19. data/spec/image-specs/scroll-mode3.case +10 -0
  20. data/spec/image-specs/scroll-mode3.png +0 -0
  21. data/spec/image-specs/sprites-doubling.case +5 -0
  22. data/spec/image-specs/sprites-doubling.png +0 -0
  23. data/spec/image-specs/sprites-mode2.case +8 -0
  24. data/spec/image-specs/sprites-mode2.png +0 -0
  25. data/spec/image-specs/sprites-mode3.case +8 -0
  26. data/spec/image-specs/sprites-mode3.png +0 -0
  27. data/spec/image-specs/sprites-ordering.case +5 -0
  28. data/spec/image-specs/sprites-ordering.png +0 -0
  29. data/spec/image-specs/text-colors.case +5 -0
  30. data/spec/image-specs/text-colors.png +0 -0
  31. data/spec/image-specs/tile-attributes-mode2.case +9 -0
  32. data/spec/image-specs/tile-attributes-mode2.png +0 -0
  33. data/spec/image-specs/tile-attributes-mode3.case +9 -0
  34. data/spec/image-specs/tile-attributes-mode3.png +0 -0
  35. data/spec/retrograph_spec.rb +133 -0
  36. data/src/retrograph.c +695 -0
  37. data/src/retrograph.h +66 -0
  38. metadata +103 -0
data/src/retrograph.c ADDED
@@ -0,0 +1,695 @@
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
+ uint32_t palette_cache[32];
95
+ /* +16 to accomodate double-width sprites */
96
+ uint32_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(uint32_t * const out_buffer, uint32_t const color) {
287
+ int i;
288
+ for (i = 0; i < RETROGRAPH_DISPLAY_WIDTH; i++) {
289
+ out_buffer[i] = color;
290
+ }
291
+ }
292
+
293
+ #define DEFINE_RENDER_CHAR(width) \
294
+ static inline void render_char ## width(uint32_t * const out_buffer, \
295
+ uint8_t const c, \
296
+ uint8_t const attrs, \
297
+ uint8_t const * const patterns, \
298
+ uint32_t const * const palette) \
299
+ { \
300
+ uint8_t pattern_row; \
301
+ uint32_t colors[2]; \
302
+ int i; \
303
+ colors[1] = palette[attrs % 16]; \
304
+ colors[0] = palette[attrs / 16]; \
305
+ pattern_row = patterns[c * TEXT_PATTERN_STRIDE]; \
306
+ for (i = 0; i < width; i++) { \
307
+ out_buffer[i] = colors[pattern_row & 1]; \
308
+ pattern_row >>= 1; \
309
+ } \
310
+ }
311
+
312
+ DEFINE_RENDER_CHAR(6)
313
+ DEFINE_RENDER_CHAR(8)
314
+
315
+ static void render_line_text_a(uint32_t *out_buffer, uint8_t const *names,
316
+ uint8_t y_fine, uint8_t const *patterns,
317
+ uint32_t const *palette, char enable_bg)
318
+ {
319
+ int i;
320
+
321
+ if (enable_bg) {
322
+ patterns += y_fine;
323
+
324
+ /* 8px padding on either side of display */
325
+ out_buffer += 8;
326
+
327
+ /* draw text columns */
328
+ for (i = 0; i < 40; i++) {
329
+ render_char6(out_buffer, names[0], names[1], patterns, palette);
330
+ out_buffer += 6;
331
+ names += 2;
332
+ }
333
+ }
334
+ }
335
+
336
+ static void render_line_text_b(uint32_t *out_buffer, uint8_t const *names,
337
+ uint8_t y_fine, uint8_t const *patterns,
338
+ uint32_t const *palette, char enable_bg)
339
+ {
340
+ int i;
341
+ if (enable_bg) {
342
+ patterns += y_fine;
343
+ for (i = 0; i < 32; i++) {
344
+ render_char8(out_buffer, names[0], names[1], patterns, palette);
345
+ out_buffer += 8;
346
+ names += 2;
347
+ }
348
+ }
349
+ }
350
+
351
+ static inline uint16_t unpack_uint16(uint8_t const * const bytes) {
352
+ return (uint16_t)((bytes[1] << 8) | bytes[0]);
353
+ }
354
+
355
+ static inline uint32_t unpack_uint32(uint8_t const * const bytes) {
356
+ return (uint32_t)((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]);
357
+ }
358
+
359
+ #define RENDER_SINGLE_PIXEL out_buffer[i] = palette[color | base_color]
360
+ #define RENDER_DOUBLE_PIXEL out_buffer[i*2] = palette[color | base_color]; \
361
+ out_buffer[i*2+1] = palette[color | base_color]
362
+
363
+ #define DEFINE_RENDER_TILE(bpp, suffix, RENDER_PIXEL, row_type, unpack_row) \
364
+ static inline void render_ ## suffix ## bpp(uint32_t *out_buffer, \
365
+ uint16_t cell, \
366
+ uint8_t const *vflip_patterns[2], \
367
+ uint32_t const *palette) \
368
+ { \
369
+ row_type pattern_row; \
370
+ uint8_t base_color; \
371
+ int i; \
372
+ { \
373
+ uint8_t const *pattern_ptr; \
374
+ pattern_ptr = vflip_patterns[(cell >> TILE_VFLIP_BIT) & 1] + \
375
+ ((cell & TILE_PATTERN_MASK) >> TILE_PATTERN_SHIFT) * \
376
+ bpp * 8; \
377
+ pattern_row = unpack_row(pattern_ptr); \
378
+ } \
379
+ base_color = (uint8_t)((cell & TILE_COLOR_MASK) >> \
380
+ (TILE_COLOR_SHIFT - TILE_COLOR_LOSS)); \
381
+ if (cell & TILE_HFLIP_MASK) { \
382
+ for (i = 0; i < 8; i++) { \
383
+ uint8_t color; \
384
+ color = (uint8_t)(pattern_row >> ((8 * bpp) - bpp)); \
385
+ if (color) { \
386
+ RENDER_PIXEL; \
387
+ } \
388
+ pattern_row <<= bpp; \
389
+ } \
390
+ } else { /* no hflip */ \
391
+ for (i = 0; i < 8; i++) { \
392
+ uint8_t color; \
393
+ color = (uint8_t)(pattern_row & ((1 << bpp) - 1)); \
394
+ if (color) { \
395
+ RENDER_PIXEL; \
396
+ } \
397
+ pattern_row >>= bpp; \
398
+ } \
399
+ } \
400
+ }
401
+
402
+ DEFINE_RENDER_TILE(2, tile, RENDER_SINGLE_PIXEL, uint16_t, unpack_uint16)
403
+ DEFINE_RENDER_TILE(2, tile_double, RENDER_DOUBLE_PIXEL, uint16_t, unpack_uint16)
404
+ DEFINE_RENDER_TILE(4, tile, RENDER_SINGLE_PIXEL, uint32_t, unpack_uint32)
405
+ DEFINE_RENDER_TILE(4, tile_double, RENDER_DOUBLE_PIXEL, uint32_t, unpack_uint32)
406
+
407
+ #define DEFINE_RENDER_BG_LINE(bpp) \
408
+ static void render_bg_line ## bpp(uint32_t *out_buffer, \
409
+ uint8_t x_coarse, uint8_t x_fine, \
410
+ uint8_t const *names, \
411
+ uint8_t const *vflip_patterns[2], \
412
+ uint16_t toggle_bits, \
413
+ uint32_t const *palette) \
414
+ { \
415
+ int i; \
416
+ uint8_t const *name_cursor; \
417
+ out_buffer += (8 - x_fine) % 8; \
418
+ x_coarse = (x_coarse + (x_fine != 0)) % 32; \
419
+ name_cursor = names + x_coarse * 2; \
420
+ /* render up to the end of the nametable row */ \
421
+ for (i = x_coarse; i < 32; i++) { \
422
+ uint16_t cell; \
423
+ cell = (uint16_t)name_cursor[0] | \
424
+ ((uint16_t)BG_ATTRS_TO_GENERIC(name_cursor[1]) << 8); \
425
+ cell ^= toggle_bits; \
426
+ if (!(cell & PRIORITY_MASK)) { \
427
+ render_tile ## bpp(out_buffer, cell, vflip_patterns, palette); \
428
+ } \
429
+ out_buffer += 8; \
430
+ name_cursor += 2; \
431
+ } \
432
+ /* render remainder of line from beginning of nametable row */ \
433
+ name_cursor = names; \
434
+ for (i = 0; i < x_coarse; i++) { \
435
+ uint16_t cell; \
436
+ cell = unpack_uint16(name_cursor) ^ toggle_bits; \
437
+ if (!(cell & PRIORITY_MASK)) { \
438
+ render_tile ## bpp(out_buffer, cell, vflip_patterns, palette); \
439
+ } \
440
+ out_buffer += 8; \
441
+ name_cursor += 2; \
442
+ } \
443
+ }
444
+
445
+ DEFINE_RENDER_BG_LINE(2)
446
+ DEFINE_RENDER_BG_LINE(4)
447
+
448
+ /* result is in sprite rows rather than display rows */
449
+ static inline unsigned compute_sprite_line(uint8_t row, uint8_t const *sprite) {
450
+ unsigned sprite_line;
451
+ /* sprite_y = sprite[1] - 1 */
452
+ sprite_line = row - (unsigned)sprite[1] + 1;
453
+ /* vertical doubling */
454
+ sprite_line >>= (SPRITE_ATTRS_TO_GENERIC(sprite[3]) &
455
+ (SPRITE_VDOUBLE_MASK >> 8)) >>
456
+ (SPRITE_VDOUBLE_BIT - 8);
457
+ return sprite_line;
458
+ }
459
+
460
+ static void render_sprites_line(uint32_t *out_buffer, uint8_t row,
461
+ uint8_t const *sprites,
462
+ uint8_t const *patterns,
463
+ uint32_t const *palette)
464
+ {
465
+ unsigned sprite_lines[MAX_SPRITES];
466
+ int i, n_sprites;
467
+ /* find the first MAX_SPRITES_PER_SCANLINE sprites spanning this line */
468
+ for (i = 0, n_sprites = 0;
469
+ i < MAX_SPRITES && n_sprites < MAX_SPRITES_PER_SCANLINE;
470
+ i++)
471
+ {
472
+ unsigned sprite_line;
473
+ /* takes double-height sprites into account */
474
+ sprite_line = compute_sprite_line(row, &sprites[i*4]);
475
+ /* row >= sprite_y && row < sprite_y + sprite_height */
476
+ if (sprite_line < 8) {
477
+ n_sprites++;
478
+ }
479
+ sprite_lines[i] = sprite_line;
480
+ }
481
+ /* work backwards, rendering sprites on this line as we encounter them */
482
+ for (i--; i >= 0; i--) {
483
+ uint8_t const * const sprite = &sprites[i*4];
484
+ unsigned sprite_line;
485
+ sprite_line = sprite_lines[i];
486
+ if (sprite_line < 8) {
487
+ uint16_t cell;
488
+ uint8_t sprite_x;
489
+ uint8_t const *vflip_patterns[2];
490
+ uint16_t render_cell;
491
+ cell = (uint16_t)sprite[2] |
492
+ ((uint16_t)SPRITE_ATTRS_TO_GENERIC(sprite[3]) << 8);
493
+ vflip_patterns[0] = patterns + 4 * sprite_line;
494
+ vflip_patterns[1] = patterns + 4 * (7 - sprite_line);
495
+ sprite_x = sprite[0];
496
+ render_cell = cell;
497
+ render_cell &= TILE_HFLIP_MASK | TILE_VFLIP_MASK |
498
+ (TILE_COLOR_MASK ^ TILE_COLOR_HI_MASK) |
499
+ (TILE_PATTERN_MASK ^ TILE_PATTERN_HI_MASK);
500
+ render_cell |= TILE_COLOR_HI_MASK;
501
+ if (cell & SPRITE_HDOUBLE_MASK) {
502
+ render_tile_double4(&out_buffer[sprite_x], render_cell,
503
+ vflip_patterns, palette);
504
+ } else {
505
+ render_tile4(&out_buffer[sprite_x], render_cell,
506
+ vflip_patterns, palette);
507
+ }
508
+ }
509
+ }
510
+ }
511
+
512
+ static void render_line_graphics_a(uint32_t *out_buffer,
513
+ uint8_t const * const names,
514
+ uint8_t x_coarse, uint8_t x_fine,
515
+ uint8_t y_fine, uint8_t row,
516
+ uint8_t const *patterns,
517
+ retrograph_addr_t pattern_base,
518
+ uint8_t const *sprites,
519
+ uint32_t const *palette,
520
+ char enable_bg, char enable_sprites)
521
+ {
522
+ uint8_t const *vflip_patterns[2];
523
+
524
+ vflip_patterns[0] = patterns + pattern_base + y_fine * 2;
525
+ vflip_patterns[1] = patterns + pattern_base + (7 - y_fine) * 2;
526
+
527
+ if (enable_bg) {
528
+ render_bg_line2(out_buffer, x_coarse, x_fine, names,
529
+ vflip_patterns, 0, palette);
530
+ }
531
+ if (enable_sprites) {
532
+ uint8_t const *sprite_patterns;
533
+ sprite_patterns = patterns + compute_sprite_pattern_base(pattern_base);
534
+ render_sprites_line(out_buffer, row, sprites, sprite_patterns,
535
+ palette);
536
+ }
537
+ if (enable_bg) {
538
+ render_bg_line2(out_buffer, x_coarse, x_fine, names,
539
+ vflip_patterns, PRIORITY_MASK, palette);
540
+ }
541
+ }
542
+
543
+ static void render_line_graphics_b(uint32_t *out_buffer, uint8_t const *names,
544
+ uint8_t x_coarse, uint8_t x_fine,
545
+ uint8_t y_fine, uint8_t row,
546
+ uint8_t const *patterns,
547
+ retrograph_addr_t pattern_base,
548
+ uint8_t const *sprites,
549
+ uint32_t const *palette,
550
+ char enable_bg, char enable_sprites)
551
+ {
552
+ uint8_t const *vflip_patterns[2];
553
+ uint16_t hi_index;
554
+
555
+ hi_index = (pattern_base != 0) << 9;
556
+ vflip_patterns[0] = patterns + y_fine * 4;
557
+ vflip_patterns[1] = patterns + (7 - y_fine) * 4;
558
+
559
+ if (enable_bg) {
560
+ render_bg_line4(out_buffer, x_coarse, x_fine, names,
561
+ vflip_patterns, hi_index, palette);
562
+ }
563
+ if (enable_sprites) {
564
+ uint8_t const *sprite_patterns;
565
+ sprite_patterns = patterns + compute_sprite_pattern_base(pattern_base);
566
+ render_sprites_line(out_buffer, row, sprites, sprite_patterns,
567
+ palette);
568
+ }
569
+ if (enable_bg) {
570
+ render_bg_line4(out_buffer, x_coarse, x_fine, names,
571
+ vflip_patterns, PRIORITY_MASK | hi_index, palette);
572
+ }
573
+ }
574
+
575
+ static void render_line(retrograph_vdu_t vdu, unsigned scanline, uint8_t *dest)
576
+ {
577
+ uint8_t const *name_row;
578
+ uint8_t bg_x_coarse;
579
+ uint8_t bg_x_fine;
580
+ uint8_t bg_y_coarse;
581
+ uint8_t bg_y_fine;
582
+
583
+ { /* scrolling */
584
+ unsigned bg_y;
585
+
586
+ if (is_graphics_mode(vdu->mode)) {
587
+ bg_y = scanline + vdu->bg_scroll_y;
588
+ } else {
589
+ /* text modes scroll vertically in full-character increments */
590
+ bg_y = scanline + (vdu->bg_scroll_y & ~7);
591
+ if (vdu->mode == TEXT_A) {
592
+ bg_y -= 12;
593
+ }
594
+ }
595
+
596
+ if (is_graphics_mode(vdu->mode)) {
597
+ bg_x_coarse = vdu->bg_scroll_x / 8;
598
+ bg_x_fine = vdu->bg_scroll_x % 8;
599
+ } else {
600
+ /* text modes have no horizontal scrolling */
601
+ bg_x_coarse = 0;
602
+ bg_x_fine = 0;
603
+ }
604
+
605
+ bg_y_coarse = (uint8_t)(bg_y / 8);
606
+ switch (vdu->mode) {
607
+ case TEXT_A:
608
+ bg_y_coarse %= 25;
609
+ break;
610
+ case TEXT_B:
611
+ bg_y_coarse %= 28;
612
+ break;
613
+ default:
614
+ bg_y_coarse %= 32;
615
+ }
616
+ bg_y_fine = (uint8_t)(bg_y % 8);
617
+ }
618
+
619
+ update_palette_cache(vdu);
620
+
621
+ switch (vdu->mode) {
622
+ case TEXT_A:
623
+ /* stride is 40 2-byte cells */
624
+ name_row = vdu->vram + vdu->name_base + bg_y_coarse * (40 * 2);
625
+ break;
626
+ default:
627
+ /* stride is 32 2-byte cells */
628
+ name_row = vdu->vram + vdu->name_base + bg_y_coarse * (32 * 2);
629
+ }
630
+
631
+ clear_row_buffer(vdu->row_buffer, vdu->palette_cache[0]);
632
+ switch (vdu->mode) {
633
+ case TEXT_A:
634
+ /* 12px padding above and below display */
635
+ if (scanline >= 12 ||
636
+ scanline < RETROGRAPH_DISPLAY_HEIGHT - 12)
637
+ {
638
+ render_line_text_a(vdu->row_buffer, name_row, bg_y_fine,
639
+ vdu->vram + vdu->text_pattern_base,
640
+ vdu->palette_cache, vdu->enable_bg);
641
+ }
642
+ break;
643
+ case TEXT_B:
644
+ render_line_text_b(vdu->row_buffer, name_row, bg_y_fine,
645
+ vdu->vram + vdu->text_pattern_base,
646
+ vdu->palette_cache, vdu->enable_bg);
647
+ break;
648
+ case GRAPHICS_A:
649
+ render_line_graphics_a(vdu->row_buffer, name_row,
650
+ bg_x_coarse, bg_x_fine, bg_y_fine,
651
+ scanline, vdu->vram,
652
+ vdu->bg_pattern_base,
653
+ vdu->sprites,
654
+ vdu->palette_cache,
655
+ vdu->enable_bg, vdu->enable_sprites);
656
+ break;
657
+ case GRAPHICS_B:
658
+ render_line_graphics_b(vdu->row_buffer, name_row,
659
+ bg_x_coarse, bg_x_fine, bg_y_fine,
660
+ scanline, vdu->vram,
661
+ vdu->bg_pattern_base,
662
+ vdu->sprites,
663
+ vdu->palette_cache,
664
+ vdu->enable_bg, vdu->enable_sprites);
665
+ break;
666
+ }
667
+ memcpy(dest, vdu->row_buffer, ROW_BYTES);
668
+ }
669
+
670
+ void retrograph_begin_frame(retrograph_vdu_t vdu) {
671
+ if (!vdu || vdu->error) {
672
+ return;
673
+ }
674
+ vdu->current_scanline = 0;
675
+ }
676
+
677
+ void retrograph_render_scanline(retrograph_vdu_t vdu, void *dest,
678
+ size_t dest_size)
679
+ {
680
+ if (!vdu || vdu->error) {
681
+ return;
682
+ }
683
+ if (dest_size < ROW_BYTES) {
684
+ return;
685
+ }
686
+
687
+ if (vdu->current_scanline < RETROGRAPH_DISPLAY_HEIGHT) {
688
+ render_line(vdu, vdu->current_scanline, (uint8_t *)dest);
689
+ vdu->current_scanline++;
690
+ } else {
691
+ update_palette_cache(vdu);
692
+ clear_row_buffer(vdu->row_buffer, vdu->palette_cache[0]);
693
+ memcpy(dest, vdu->row_buffer, ROW_BYTES);
694
+ }
695
+ }