retrograph 0.5-x86-mswin32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +19 -0
- data/README +264 -0
- data/Rakefile +71 -0
- data/ext/retrograph/extconf.rb +6 -0
- data/ext/retrograph/retrograph.c +324 -0
- data/lib/retrograph/sdl.rb +26 -0
- data/lib/retrograph.rb +29 -0
- data/lib/retrograph.so +0 -0
- data/spec/image-specs/color-blue-bits.case +2 -0
- data/spec/image-specs/color-blue-bits.png +0 -0
- data/spec/image-specs/color-green-bits.case +2 -0
- data/spec/image-specs/color-green-bits.png +0 -0
- data/spec/image-specs/color-red-bits.case +2 -0
- data/spec/image-specs/color-red-bits.png +0 -0
- data/spec/image-specs/default-screen-black.case +1 -0
- data/spec/image-specs/default-screen-black.png +0 -0
- data/spec/image-specs/default-screen-color-0.case +2 -0
- data/spec/image-specs/default-screen-color-0.png +0 -0
- data/spec/image-specs/scroll-mode1.case +7 -0
- data/spec/image-specs/scroll-mode1.png +0 -0
- data/spec/image-specs/scroll-mode2.case +10 -0
- data/spec/image-specs/scroll-mode2.png +0 -0
- data/spec/image-specs/scroll-mode3.case +10 -0
- data/spec/image-specs/scroll-mode3.png +0 -0
- data/spec/image-specs/sprites-doubling.case +5 -0
- data/spec/image-specs/sprites-doubling.png +0 -0
- data/spec/image-specs/sprites-mode2.case +8 -0
- data/spec/image-specs/sprites-mode2.png +0 -0
- data/spec/image-specs/sprites-mode3.case +8 -0
- data/spec/image-specs/sprites-mode3.png +0 -0
- data/spec/image-specs/sprites-ordering.case +5 -0
- data/spec/image-specs/sprites-ordering.png +0 -0
- data/spec/image-specs/text-colors.case +5 -0
- data/spec/image-specs/text-colors.png +0 -0
- data/spec/image-specs/tile-attributes-mode2.case +9 -0
- data/spec/image-specs/tile-attributes-mode2.png +0 -0
- data/spec/image-specs/tile-attributes-mode3.case +9 -0
- data/spec/image-specs/tile-attributes-mode3.png +0 -0
- data/spec/retrograph_spec.rb +134 -0
- data/src/retrograph.c +697 -0
- data/src/retrograph.h +67 -0
- 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
|
+
}
|