main
vdp.c
1/*
2 Copyright 2013 Michael Pavone
3 This file is part of BlastEm.
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
5*/
6#include "vdp.h"
7#include "blastem.h"
8#include <stdlib.h>
9#include <string.h>
10#include "render.h"
11#include "util.h"
12
13#define NTSC_INACTIVE_START 224
14#define PAL_INACTIVE_START 240
15#define MODE4_INACTIVE_START 192
16#define BUF_BIT_PRIORITY 0x40
17#define MAP_BIT_PRIORITY 0x8000
18#define MAP_BIT_H_FLIP 0x800
19#define MAP_BIT_V_FLIP 0x1000
20
21#define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1)
22#define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2)
23
24#define MCLKS_SLOT_H40 16
25#define MCLKS_SLOT_H32 20
26#define VINT_SLOT_H40 0 //21 slots before HSYNC, 16 during, 10 after
27#define VINT_SLOT_H32 0 //old value was 23, but recent tests suggest the actual value is close to the H40 one
28#define VINT_SLOT_MODE4 4
29#define HSYNC_SLOT_H40 230
30#define HSYNC_END_H40 (HSYNC_SLOT_H40+17)
31#define HBLANK_START_H40 178 //should be 179 according to Nemesis, but 178 seems to fit slightly better with my test ROM results
32#define HBLANK_END_H40 0 //should be 5.5 according to Nemesis, but 0 seems to fit better with my test ROM results
33#define HBLANK_START_H32 233 //should be 147 according to Nemesis which is very different from my test ROM result
34#define HBLANK_END_H32 0 //should be 5 according to Nemesis, but 0 seems to fit better with my test ROM results
35#define LINE_CHANGE_H40 165
36#define LINE_CHANGE_H32 133
37#define LINE_CHANGE_MODE4 249
38#define VBLANK_START_H40 (LINE_CHANGE_H40+2)
39#define VBLANK_START_H32 (LINE_CHANGE_H32+2)
40#define FIFO_LATENCY 3
41
42#define BORDER_TOP_V24 27
43#define BORDER_TOP_V28 11
44#define BORDER_TOP_V24_PAL 54
45#define BORDER_TOP_V28_PAL 38
46#define BORDER_TOP_V30_PAL 30
47
48#define BORDER_BOT_V24 24
49#define BORDER_BOT_V28 8
50#define BORDER_BOT_V24_PAL 48
51#define BORDER_BOT_V28_PAL 32
52#define BORDER_BOT_V30_PAL 24
53
54#define INVALID_LINE (PAL_INACTIVE_START+BORDER_TOP_V30_PAL+BORDER_BOT_V30_PAL)
55
56enum {
57 INACTIVE = 0,
58 PREPARING, //used for line 0x1FF
59 ACTIVE
60};
61
62static int32_t color_map[1 << 12];
63static uint16_t mode4_address_map[0x4000];
64static uint32_t planar_to_chunky[256];
65static uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255};
66
67static uint8_t debug_base[][3] = {
68 {127, 127, 127}, //BG
69 {0, 0, 127}, //A
70 {127, 0, 0}, //Window
71 {0, 127, 0}, //B
72 {127, 0, 127} //Sprites
73};
74
75static void update_video_params(vdp_context *context)
76{
77 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
78 if (context->regs[REG_MODE_2] & BIT_PAL) {
79 if (context->flags2 & FLAG2_REGION_PAL) {
80 context->inactive_start = PAL_INACTIVE_START;
81 context->border_top = BORDER_TOP_V30_PAL;
82 context->border_bot = BORDER_BOT_V30_PAL;
83 } else {
84 //the behavior here is rather weird and needs more investigation
85 context->inactive_start = 0xF0;
86 context->border_top = 1;
87 context->border_bot = 3;
88 }
89 } else {
90 context->inactive_start = NTSC_INACTIVE_START;
91 if (context->flags2 & FLAG2_REGION_PAL) {
92 context->border_top = BORDER_TOP_V28_PAL;
93 context->border_bot = BORDER_BOT_V28_PAL;
94 } else {
95 context->border_top = BORDER_TOP_V28;
96 context->border_bot = BORDER_TOP_V28;
97 }
98 }
99 if (context->regs[REG_MODE_4] & BIT_H40) {
100 context->max_sprites_frame = MAX_SPRITES_FRAME;
101 context->max_sprites_line = MAX_SPRITES_LINE;
102 } else {
103 context->max_sprites_frame = MAX_SPRITES_FRAME_H32;
104 context->max_sprites_line = MAX_SPRITES_LINE_H32;
105 }
106 if (context->state == INACTIVE) {
107 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
108 if (context->vcounter < context->inactive_start) {
109 context->state = ACTIVE;
110 } else if (context->vcounter == 0x1FF) {
111 context->state = PREPARING;
112 }
113 }
114 } else {
115 context->inactive_start = MODE4_INACTIVE_START;
116 if (context->flags2 & FLAG2_REGION_PAL) {
117 context->border_top = BORDER_TOP_V24_PAL;
118 context->border_bot = BORDER_BOT_V24_PAL;
119 } else {
120 context->border_top = BORDER_TOP_V24;
121 context->border_bot = BORDER_BOT_V24;
122 }
123 if (!(context->regs[REG_MODE_1] & BIT_MODE_4)){
124 context->state = INACTIVE;
125 } else if (context->state == INACTIVE) {
126 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
127 if (context->vcounter < context->inactive_start) {
128 context->state = ACTIVE;
129 }
130 else if (context->vcounter == 0x1FF) {
131 context->state = PREPARING;
132 }
133 }
134 }
135}
136
137static uint8_t color_map_init_done;
138
139vdp_context *init_vdp_context(uint8_t region_pal)
140{
141 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE);
142 if (headless) {
143 context->output = malloc(LINEBUF_SIZE * sizeof(uint32_t));
144 context->output_pitch = 0;
145 } else {
146 context->cur_buffer = VIDEO_BUFFER_ODD;
147 context->fb = render_get_video_buffer(VIDEO_BUFFER_ODD, &context->output_pitch);
148 }
149 context->sprite_draws = MAX_DRAWS;
150 context->fifo_write = 0;
151 context->fifo_read = -1;
152 context->regs[REG_HINT] = context->hint_counter = 0xFF;
153
154 if (!color_map_init_done) {
155 uint8_t b,g,r;
156 for (uint16_t color = 0; color < (1 << 12); color++) {
157 if (color & FBUF_SHADOW) {
158 b = levels[(color >> 9) & 0x7];
159 g = levels[(color >> 5) & 0x7];
160 r = levels[(color >> 1) & 0x7];
161 } else if(color & FBUF_HILIGHT) {
162 b = levels[((color >> 9) & 0x7) + 7];
163 g = levels[((color >> 5) & 0x7) + 7];
164 r = levels[((color >> 1) & 0x7) + 7];
165 } else if(color & FBUF_MODE4) {
166 b = levels[(color >> 4 & 0xC) | (color >> 6 & 0x2)];
167 g = levels[(color >> 2 & 0x8) | (color >> 1 & 0x4) | (color >> 4 & 0x2)];
168 r = levels[(color << 1 & 0xC) | (color >> 1 & 0x2)];
169 } else {
170 b = levels[(color >> 8) & 0xE];
171 g = levels[(color >> 4) & 0xE];
172 r = levels[color & 0xE];
173 }
174 color_map[color] = render_map_color(r, g, b);
175 }
176 for (uint16_t mode4_addr = 0; mode4_addr < 0x4000; mode4_addr++)
177 {
178 uint16_t mode5_addr = mode4_addr & 0x3DFD;
179 mode5_addr |= mode4_addr << 8 & 0x200;
180 mode5_addr |= mode4_addr >> 8 & 2;
181 mode4_address_map[mode4_addr] = mode5_addr;
182 }
183 for (uint32_t planar = 0; planar < 256; planar++)
184 {
185 uint32_t chunky = 0;
186 for (int bit = 7; bit >= 0; bit--)
187 {
188 chunky = chunky << 4;
189 chunky |= planar >> bit & 1;
190 }
191 planar_to_chunky[planar] = chunky;
192 }
193 color_map_init_done = 1;
194 }
195 for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++)
196 {
197 uint8_t src = color & DBG_SRC_MASK;
198 if (src > DBG_SRC_S) {
199 context->debugcolors[color] = 0;
200 } else {
201 uint8_t r,g,b;
202 b = debug_base[src][0];
203 g = debug_base[src][1];
204 r = debug_base[src][2];
205 if (color & DBG_PRIORITY)
206 {
207 if (b) {
208 b += 48;
209 }
210 if (g) {
211 g += 48;
212 }
213 if (r) {
214 r += 48;
215 }
216 }
217 if (color & DBG_SHADOW) {
218 b /= 2;
219 g /= 2;
220 r /=2 ;
221 }
222 if (color & DBG_HILIGHT) {
223 if (b) {
224 b += 72;
225 }
226 if (g) {
227 g += 72;
228 }
229 if (r) {
230 r += 72;
231 }
232 }
233 context->debugcolors[color] = render_map_color(r, g, b);
234 }
235 }
236 if (region_pal) {
237 context->flags2 |= FLAG2_REGION_PAL;
238 }
239 update_video_params(context);
240 if (!headless) {
241 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
242 }
243 return context;
244}
245
246void vdp_free(vdp_context *context)
247{
248 free(context);
249}
250
251static int is_refresh(vdp_context * context, uint32_t slot)
252{
253 if (context->regs[REG_MODE_4] & BIT_H40) {
254 return slot == 250 || slot == 26 || slot == 59 || slot == 90 || slot == 122 || slot == 154;
255 } else {
256 //TODO: Figure out which slots are refresh when display is off in 32-cell mode
257 //These numbers are guesses based on H40 numbers
258 return slot == 243 || slot == 19 || slot == 51 || slot == 83 || slot == 115;
259 //The numbers below are the refresh slots during active display
260 //return (slot == 29 || slot == 61 || slot == 93 || slot == 125);
261 }
262}
263
264static void increment_address(vdp_context *context)
265{
266 context->address += context->regs[REG_AUTOINC];
267 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
268 context->address++;
269 }
270}
271
272static void render_sprite_cells(vdp_context * context)
273{
274 sprite_draw * d = context->sprite_draw_list + context->cur_slot;
275 context->serial_address = d->address;
276 if (context->cur_slot >= context->sprite_draws) {
277
278 uint16_t dir;
279 int16_t x;
280 if (d->h_flip) {
281 x = d->x_pos + 7;
282 dir = -1;
283 } else {
284 x = d->x_pos;
285 dir = 1;
286 }
287 //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x);
288 context->cur_slot--;
289 for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) {
290 if (x >= 0 && x < 320) {
291 if (!(context->linebuf[x] & 0xF)) {
292 context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority;
293 } else if (context->vdpmem[address] >> 4) {
294 context->flags2 |= FLAG2_SPRITE_COLLIDE;
295 }
296 }
297 x += dir;
298 if (x >= 0 && x < 320) {
299 if (!(context->linebuf[x] & 0xF)) {
300 context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority;
301 } else if (context->vdpmem[address] & 0xF) {
302 context->flags2 |= FLAG2_SPRITE_COLLIDE;
303 }
304 }
305 x += dir;
306 }
307 } else {
308 context->cur_slot--;
309 }
310}
311
312static void fetch_sprite_cells_mode4(vdp_context * context)
313{
314 if (context->sprite_index >= context->sprite_draws) {
315 sprite_draw * d = context->sprite_draw_list + context->sprite_index;
316 uint32_t address = mode4_address_map[d->address & 0x3FFF];
317 context->fetch_tmp[0] = context->vdpmem[address];
318 context->fetch_tmp[1] = context->vdpmem[address + 1];
319 }
320}
321
322static void render_sprite_cells_mode4(vdp_context * context)
323{
324 if (context->sprite_index >= context->sprite_draws) {
325 sprite_draw * d = context->sprite_draw_list + context->sprite_index;
326 uint32_t pixels = planar_to_chunky[context->fetch_tmp[0]] << 1;
327 pixels |= planar_to_chunky[context->fetch_tmp[1]];
328 uint32_t address = mode4_address_map[(d->address + 2) & 0x3FFF];
329 pixels |= planar_to_chunky[context->vdpmem[address]] << 3;
330 pixels |= planar_to_chunky[context->vdpmem[address + 1]] << 2;
331 int x = d->x_pos & 0xFF;
332 for (int i = 28; i >= 0; i -= 4, x++)
333 {
334 if (context->linebuf[x] && (pixels >> i & 0xF)) {
335 if (
336 ((context->regs[REG_MODE_1] & BIT_SPRITE_8PX) && x > 8)
337 || ((!(context->regs[REG_MODE_1] & BIT_SPRITE_8PX)) && x < 256)
338 ) {
339 context->flags2 |= FLAG2_SPRITE_COLLIDE;
340 }
341 } else {
342 context->linebuf[x] = pixels >> i & 0xF;
343 }
344 }
345 context->sprite_index--;
346 }
347}
348
349static uint32_t mode5_sat_address(vdp_context *context)
350{
351 uint32_t addr = context->regs[REG_SAT] << 9;
352 if (!(context->regs[REG_MODE_2] & BIT_128K_VRAM)) {
353 addr &= 0xFFFF;
354 }
355 if (context->regs[REG_MODE_4] & BIT_H40) {
356 addr &= 0x1FC00;
357 }
358 return addr;
359}
360
361void vdp_print_sprite_table(vdp_context * context)
362{
363 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
364 uint16_t sat_address = mode5_sat_address(context);
365 uint16_t current_index = 0;
366 uint8_t count = 0;
367 do {
368 uint16_t address = current_index * 8 + sat_address;
369 uint16_t cache_address = current_index * 4;
370 uint8_t height = ((context->sat_cache[cache_address+2] & 0x3) + 1) * 8;
371 uint8_t width = (((context->sat_cache[cache_address+2] >> 2) & 0x3) + 1) * 8;
372 int16_t y = ((context->sat_cache[cache_address] & 0x3) << 8 | context->sat_cache[cache_address+1]) & 0x1FF;
373 int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF;
374 uint16_t link = context->sat_cache[cache_address+3] & 0x7F;
375 uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3;
376 uint8_t pri = context->vdpmem[address + 4] >> 7;
377 uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;
378 printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
379 current_index = link;
380 count++;
381 } while (current_index != 0 && count < 80);
382 } else {
383 uint16_t sat_address = (context->regs[REG_SAT] & 0x7E) << 7;
384 for (int i = 0; i < 64; i++)
385 {
386 uint8_t y = context->vdpmem[mode4_address_map[sat_address + (i ^ 1)]];
387 if (y == 0xD0) {
388 break;
389 }
390 uint8_t x = context->vdpmem[mode4_address_map[sat_address + 0x80 + i*2 + 1]];
391 uint16_t tile_address = context->vdpmem[mode4_address_map[sat_address + 0x80 + i*2]] * 32
392 + (context->regs[REG_STILE_BASE] << 11 & 0x2000);
393 if (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) {
394 tile_address &= ~32;
395 }
396 printf("Sprite %d: X=%d, Y=%d, Pat=%X\n", i, x, y, tile_address);
397 }
398 }
399}
400
401#define VRAM_READ 0 //0000
402#define VRAM_WRITE 1 //0001
403//2 would trigger register write 0010
404#define CRAM_WRITE 3 //0011
405#define VSRAM_READ 4 //0100
406#define VSRAM_WRITE 5//0101
407//6 would trigger regsiter write 0110
408//7 is a mystery //0111
409#define CRAM_READ 8 //1000
410//9 is also a mystery //1001
411//A would trigger register write 1010
412//B is a mystery 1011
413#define VRAM_READ8 0xC //1100
414//D is a mystery 1101
415//E would trigger register write 1110
416//F is a mystery 1111
417
418//Possible theory on how bits work
419//CD0 = Read/Write flag
420//CD2,(CD1|CD3) = RAM type
421// 00 = VRAM
422// 01 = CRAM
423// 10 = VSRAM
424// 11 = VRAM8
425//Would result in
426// 7 = VRAM8 write
427// 9 = CRAM write alias
428// B = CRAM write alias
429// D = VRAM8 write alias
430// F = VRAM8 write alais
431
432#define DMA_START 0x20
433
434static const char * cd_name(uint8_t cd)
435{
436 switch (cd & 0xF)
437 {
438 case VRAM_READ:
439 return "VRAM read";
440 case VRAM_WRITE:
441 return "VRAM write";
442 case CRAM_WRITE:
443 return "CRAM write";
444 case VSRAM_READ:
445 return "VSRAM read";
446 case VSRAM_WRITE:
447 return "VSRAM write";
448 case VRAM_READ8:
449 return "VRAM read (undocumented 8-bit mode)";
450 default:
451 return "invalid";
452 }
453}
454
455void vdp_print_reg_explain(vdp_context * context)
456{
457 char * hscroll[] = {"full", "7-line", "cell", "line"};
458 printf("**Mode Group**\n"
459 "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n"
460 "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d, %dK VRAM\n"
461 "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n"
462 "0C: %.2X | Width: %d, Shadow/Highlight: %s\n",
463 context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", (context->regs[REG_MODE_1] & BIT_PAL_SEL) != 0,
464 context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled",
465 context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled",
466 context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, context->regs[REG_MODE_1] & BIT_128K_VRAM ? 128 : 64,
467 context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full",
468 hscroll[context->regs[REG_MODE_3] & 0x3],
469 context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled");
470 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
471 printf("\n**Table Group**\n"
472 "02: %.2X | Scroll A Name Table: $%.4X\n"
473 "03: %.2X | Window Name Table: $%.4X\n"
474 "04: %.2X | Scroll B Name Table: $%.4X\n"
475 "05: %.2X | Sprite Attribute Table: $%.4X\n"
476 "0D: %.2X | HScroll Data Table: $%.4X\n",
477 context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0x38) << 10,
478 context->regs[REG_WINDOW], (context->regs[REG_WINDOW] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3C : 0x3E)) << 10,
479 context->regs[REG_SCROLL_B], (context->regs[REG_SCROLL_B] & 0x7) << 13,
480 context->regs[REG_SAT], mode5_sat_address(context),
481 context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x3F) << 10);
482 } else {
483 printf("\n**Table Group**\n"
484 "02: %.2X | Background Name Table: $%.4X\n"
485 "05: %.2X | Sprite Attribute Table: $%.4X\n"
486 "06: %.2X | Sprite Tile Base: $%.4X\n"
487 "08: %.2X | Background X Scroll: %d\n"
488 "09: %.2X | Background Y Scroll: %d\n",
489 context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0xE) << 10,
490 context->regs[REG_SAT], (context->regs[REG_SAT] & 0x7E) << 7,
491 context->regs[REG_STILE_BASE], (context->regs[REG_STILE_BASE] & 2) << 11,
492 context->regs[REG_X_SCROLL], context->regs[REG_X_SCROLL],
493 context->regs[REG_Y_SCROLL], context->regs[REG_Y_SCROLL]);
494
495 }
496 char * sizes[] = {"32", "64", "invalid", "128"};
497 printf("\n**Misc Group**\n"
498 "07: %.2X | Backdrop Color: $%X\n"
499 "0A: %.2X | H-Int Counter: %u\n"
500 "0F: %.2X | Auto-increment: $%X\n"
501 "10: %.2X | Scroll A/B Size: %sx%s\n",
502 context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR],
503 context->regs[REG_HINT], context->regs[REG_HINT],
504 context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
505 context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
506 char * src_types[] = {"68K", "68K", "Copy", "Fill"};
507 printf("\n**DMA Group**\n"
508 "13: %.2X |\n"
509 "14: %.2X | DMA Length: $%.4X words\n"
510 "15: %.2X |\n"
511 "16: %.2X |\n"
512 "17: %.2X | DMA Source Address: $%.6X, Type: %s\n",
513 context->regs[REG_DMALEN_L],
514 context->regs[REG_DMALEN_H], context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L],
515 context->regs[REG_DMASRC_L],
516 context->regs[REG_DMASRC_M],
517 context->regs[REG_DMASRC_H],
518 context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1,
519 src_types[context->regs[REG_DMASRC_H] >> 6 & 3]);
520 uint8_t old_flags = context->flags;
521 uint8_t old_flags2 = context->flags2;
522 printf("\n**Internal Group**\n"
523 "Address: %X\n"
524 "CD: %X - %s\n"
525 "Pending: %s\n"
526 "VCounter: %d\n"
527 "HCounter: %d\n"
528 "VINT Pending: %s\n"
529 "HINT Pending: %s\n"
530 "Status: %X\n",
531 context->address, context->cd, cd_name(context->cd),
532 (context->flags & FLAG_PENDING) ? "word" : (context->flags2 & FLAG2_BYTE_PENDING) ? "byte" : "none",
533 context->vcounter, context->hslot*2, (context->flags2 & FLAG2_VINT_PENDING) ? "true" : "false",
534 (context->flags2 & FLAG2_HINT_PENDING) ? "true" : "false", vdp_control_port_read(context));
535 //restore flags as calling vdp_control_port_read can change them
536 context->flags = old_flags;
537 context->flags2 = old_flags2;
538}
539
540static uint8_t is_active(vdp_context *context)
541{
542 return context->state != INACTIVE && (context->regs[REG_MODE_2] & BIT_DISP_EN) != 0;
543}
544
545static void scan_sprite_table(uint32_t line, vdp_context * context)
546{
547 if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line) {
548 line += 1;
549 uint16_t ymask, ymin;
550 uint8_t height_mult;
551 if (context->double_res) {
552 line *= 2;
553 if (context->flags2 & FLAG2_EVEN_FIELD) {
554 line++;
555 }
556 ymask = 0x3FF;
557 ymin = 256;
558 height_mult = 16;
559 } else {
560 ymask = 0x1FF;
561 ymin = 128;
562 height_mult = 8;
563 }
564 context->sprite_index &= 0x7F;
565 //TODO: Implement squirelly behavior documented by Kabuto
566 if (context->sprite_index >= context->max_sprites_frame) {
567 context->sprite_index = 0;
568 return;
569 }
570 uint16_t address = context->sprite_index * 4;
571 line += ymin;
572 line &= ymask;
573 uint16_t y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask;
574 uint8_t height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult;
575 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
576 if (y <= line && line < (y + height)) {
577 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
578 context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
579 context->sprite_info_list[context->slot_counter++].index = context->sprite_index;
580 }
581 context->sprite_index = context->sat_cache[address+3] & 0x7F;
582 if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line)
583 {
584 //TODO: Implement squirelly behavior documented by Kabuto
585 if (context->sprite_index >= context->max_sprites_frame) {
586 context->sprite_index = 0;
587 return;
588 }
589 address = context->sprite_index * 4;
590 y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask;
591 height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult;
592 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
593 if (y <= line && line < (y + height)) {
594 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
595 context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
596 context->sprite_info_list[context->slot_counter++].index = context->sprite_index;
597 }
598 context->sprite_index = context->sat_cache[address+3] & 0x7F;
599 }
600 }
601 //TODO: Seems like the overflow flag should be set here if we run out of sprite info slots without hitting the end of the list
602}
603
604static void scan_sprite_table_mode4(vdp_context * context)
605{
606 if (context->sprite_index < MAX_SPRITES_FRAME_H32) {
607 uint32_t line = context->vcounter;
608 line &= 0xFF;
609
610 uint32_t sat_address = mode4_address_map[(context->regs[REG_SAT] << 7 & 0x3F00) + context->sprite_index];
611 uint32_t y = context->vdpmem[sat_address+1];
612 uint32_t size = (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) ? 16 : 8;
613
614 if (y == 0xd0) {
615 context->sprite_index = MAX_SPRITES_FRAME_H32;
616 return;
617 } else {
618 if (y <= line && line < (y + size)) {
619 if (!context->slot_counter) {
620 context->sprite_index = MAX_SPRITES_FRAME_H32;
621 context->flags |= FLAG_DOT_OFLOW;
622 return;
623 }
624 context->sprite_info_list[--(context->slot_counter)].size = size;
625 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
626 context->sprite_info_list[context->slot_counter].y = y;
627 }
628 context->sprite_index++;
629 }
630
631 if (context->sprite_index < MAX_SPRITES_FRAME_H32) {
632 y = context->vdpmem[sat_address];
633 if (y == 0xd0) {
634 context->sprite_index = MAX_SPRITES_FRAME_H32;
635 return;
636 } else {
637 if (y <= line && line < (y + size)) {
638 if (!context->slot_counter) {
639 context->sprite_index = MAX_SPRITES_FRAME_H32;
640 context->flags |= FLAG_DOT_OFLOW;
641 return;
642 }
643 context->sprite_info_list[--(context->slot_counter)].size = size;
644 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
645 context->sprite_info_list[context->slot_counter].y = y;
646 }
647 context->sprite_index++;
648 }
649 }
650
651 }
652}
653
654static void read_sprite_x(uint32_t line, vdp_context * context)
655{
656 if (context->cur_slot == context->max_sprites_line) {
657 context->cur_slot = 0;
658 }
659 if (context->cur_slot < context->slot_counter) {
660 if (context->sprite_draws) {
661 line += 1;
662 //in tiles
663 uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1;
664 //in pixels
665 uint8_t height = ((context->sprite_info_list[context->cur_slot].size & 0x3) + 1) * 8;
666 if (context->double_res) {
667 line *= 2;
668 if (context->flags2 & FLAG2_EVEN_FIELD) {
669 line++;
670 }
671 height *= 2;
672 }
673 uint16_t ymask, ymin;
674 if (context->double_res) {
675 ymask = 0x3FF;
676 ymin = 256;
677 } else {
678 ymask = 0x1FF;
679 ymin = 128;
680 }
681 uint16_t att_addr = mode5_sat_address(context) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
682 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
683 uint8_t pal_priority = (tileinfo >> 9) & 0x70;
684 uint8_t row;
685 uint16_t cache_addr = context->sprite_info_list[context->cur_slot].index * 4;
686 line = (line + ymin) & ymask;
687 int16_t y = ((context->sat_cache[cache_addr] << 8 | context->sat_cache[cache_addr+1]) & ymask)/* - ymin*/;
688 if (tileinfo & MAP_BIT_V_FLIP) {
689 row = (y + height - 1) - line;
690 } else {
691 row = line-y;
692 }
693 row &= ymask >> 4;
694 uint16_t address;
695 if (context->double_res) {
696 address = ((tileinfo & 0x3FF) << 6) + row * 4;
697 } else {
698 address = ((tileinfo & 0x7FF) << 5) + row * 4;
699 }
700 int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF;
701 if (x) {
702 context->flags |= FLAG_CAN_MASK;
703 } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) {
704 context->flags |= FLAG_MASKED;
705 }
706
707 context->flags &= ~FLAG_DOT_OFLOW;
708 int16_t i;
709 if (context->flags & FLAG_MASKED) {
710 for (i=0; i < width && context->sprite_draws; i++) {
711 --context->sprite_draws;
712 context->sprite_draw_list[context->sprite_draws].x_pos = -128;
713 context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4;
714 }
715 } else {
716 x -= 128;
717 int16_t base_x = x;
718 int16_t dir;
719 if (tileinfo & MAP_BIT_H_FLIP) {
720 x += (width-1) * 8;
721 dir = -8;
722 } else {
723 dir = 8;
724 }
725 //printf("Sprite %d | x: %d, y: %d, width: %d, height: %d, pal_priority: %X, row: %d, tile addr: %X\n", context->sprite_info_list[context->cur_slot].index, x, context->sprite_info_list[context->cur_slot].y, width, height, pal_priority, row, address);
726 for (i=0; i < width && context->sprite_draws; i++, x += dir) {
727 --context->sprite_draws;
728 context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4;
729 context->sprite_draw_list[context->sprite_draws].x_pos = x;
730 context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority;
731 context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0;
732 }
733 }
734 //Used to be i < width
735 //TODO: Confirm this is the right condition on hardware
736 if (!context->sprite_draws) {
737 context->flags |= FLAG_DOT_OFLOW;
738 }
739 } else {
740 context->flags |= FLAG_DOT_OFLOW;
741 }
742 }
743 context->cur_slot++;
744}
745
746static void read_sprite_x_mode4(vdp_context * context)
747{
748 if (context->cur_slot >= context->slot_counter) {
749 uint32_t address = (context->regs[REG_SAT] << 7 & 0x3F00) + 0x80 + context->sprite_info_list[context->cur_slot].index * 2;
750 address = mode4_address_map[address];
751 --context->sprite_draws;
752 uint32_t tile_address = context->vdpmem[address] * 32 + (context->regs[REG_STILE_BASE] << 11 & 0x2000);
753 if (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) {
754 tile_address &= ~32;
755 }
756 tile_address += (context->vcounter - context->sprite_info_list[context->cur_slot].y)* 4;
757 context->sprite_draw_list[context->sprite_draws].x_pos = context->vdpmem[address + 1];
758 context->sprite_draw_list[context->sprite_draws].address = tile_address;
759 context->cur_slot--;
760 }
761}
762
763#define CRAM_BITS 0xEEE
764#define VSRAM_BITS 0x7FF
765#define VSRAM_DIRTY_BITS 0xF800
766
767//rough estimate of slot number at which border display starts
768#define BG_START_SLOT 6
769
770static void update_color_map(vdp_context *context, uint16_t index, uint16_t value)
771{
772 context->colors[index] = color_map[value & CRAM_BITS];
773 context->colors[index + SHADOW_OFFSET] = color_map[(value & CRAM_BITS) | FBUF_SHADOW];
774 context->colors[index + HIGHLIGHT_OFFSET] = color_map[(value & CRAM_BITS) | FBUF_HILIGHT];
775 context->colors[index + MODE4_OFFSET] = color_map[(value & CRAM_BITS) | FBUF_MODE4];
776}
777
778void write_cram_internal(vdp_context * context, uint16_t addr, uint16_t value)
779{
780 context->cram[addr] = value;
781 update_color_map(context, addr, value);
782}
783
784static void write_cram(vdp_context * context, uint16_t address, uint16_t value)
785{
786 uint16_t addr;
787 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
788 addr = (address/2) & (CRAM_SIZE-1);
789 } else {
790 addr = address & 0x1F;
791 value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00);
792 }
793 write_cram_internal(context, addr, value);
794
795 if (context->hslot >= BG_START_SLOT && (
796 context->vcounter < context->inactive_start + context->border_bot
797 || context->vcounter > 0x200 - context->border_top
798 )) {
799 uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2;
800 if (context->hslot < bg_end_slot) {
801 uint32_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET];
802 context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color;
803 }
804 }
805}
806
807static void vdp_advance_dma(vdp_context * context)
808{
809 context->regs[REG_DMASRC_L] += 1;
810 if (!context->regs[REG_DMASRC_L]) {
811 context->regs[REG_DMASRC_M] += 1;
812 }
813 context->address += context->regs[REG_AUTOINC];
814 uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1;
815 context->regs[REG_DMALEN_H] = dma_len >> 8;
816 context->regs[REG_DMALEN_L] = dma_len;
817 if (!dma_len) {
818 context->flags &= ~FLAG_DMA_RUN;
819 context->cd &= 0xF;
820 }
821}
822
823static void vdp_check_update_sat(vdp_context *context, uint32_t address, uint16_t value)
824{
825 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
826 if (!(address & 4)) {
827 uint32_t sat_address = mode5_sat_address(context);
828 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
829 uint16_t cache_address = address - sat_address;
830 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
831 context->sat_cache[cache_address] = value >> 8;
832 context->sat_cache[cache_address^1] = value;
833 }
834 }
835 }
836}
837
838void vdp_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t value)
839{
840 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
841 if (!(address & 4)) {
842 uint32_t sat_address = mode5_sat_address(context);
843 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
844 uint16_t cache_address = address - sat_address;
845 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
846 context->sat_cache[cache_address] = value;
847 }
848 }
849 }
850}
851
852static void write_vram_word(vdp_context *context, uint32_t address, uint16_t value)
853{
854 address = (address & 0x3FC) | (address >> 1 & 0xFC01) | (address >> 9 & 0x2);
855 address ^= 1;
856 //TODO: Support an option to actually have 128KB of VRAM
857 context->vdpmem[address] = value;
858}
859
860static void write_vram_byte(vdp_context *context, uint32_t address, uint8_t value)
861{
862 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
863 address &= 0xFFFF;
864 } else {
865 address = mode4_address_map[address & 0x3FFF];
866 }
867 context->vdpmem[address] = value;
868}
869
870#define DMA_FILL 0x80
871#define DMA_COPY 0xC0
872#define DMA_TYPE_MASK 0xC0
873static void external_slot(vdp_context * context)
874{
875 if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL && context->fifo_read < 0) {
876 context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1);
877 fifo_entry * cur = context->fifo + context->fifo_read;
878 cur->cycle = context->cycles;
879 cur->address = context->address;
880 cur->partial = 1;
881 vdp_advance_dma(context);
882 }
883 fifo_entry * start = context->fifo + context->fifo_read;
884 if (context->fifo_read >= 0 && start->cycle <= context->cycles) {
885 switch (start->cd & 0xF)
886 {
887 case VRAM_WRITE:
888 if ((context->regs[REG_MODE_2] & (BIT_128K_VRAM|BIT_MODE_5)) == (BIT_128K_VRAM|BIT_MODE_5)) {
889 vdp_check_update_sat(context, start->address, start->value);
890 write_vram_word(context, start->address, start->value);
891 } else {
892 uint8_t byte = start->partial == 1 ? start->value >> 8 : start->value;
893 vdp_check_update_sat_byte(context, start->address ^ 1, byte);
894 write_vram_byte(context, start->address ^ 1, byte);
895 if (!start->partial) {
896 start->address = start->address ^ 1;
897 start->partial = 1;
898 //skip auto-increment and removal of entry from fifo
899 return;
900 }
901 }
902 break;
903 case CRAM_WRITE: {
904 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1));
905 if (start->partial == 3) {
906 uint16_t val;
907 if ((start->address & 1) && (context->regs[REG_MODE_2] & BIT_MODE_5)) {
908 val = (context->cram[start->address >> 1 & (CRAM_SIZE-1)] & 0xFF) | start->value << 8;
909 } else {
910 uint16_t address = (context->regs[REG_MODE_2] & BIT_MODE_5) ? start->address >> 1 & (CRAM_SIZE-1) : start->address & 0x1F;
911 val = (context->cram[address] & 0xFF00) | start->value;
912 }
913 write_cram(context, start->address, val);
914 } else {
915 write_cram(context, start->address, start->partial ? context->fifo[context->fifo_write].value : start->value);
916 }
917 break;
918 }
919 case VSRAM_WRITE:
920 if (((start->address/2) & 63) < VSRAM_SIZE) {
921 //printf("VSRAM Write: %X to %X @ frame: %d, vcounter: %d, hslot: %d, cycle: %d\n", start->value, start->address, context->frame, context->vcounter, context->hslot, context->cycles);
922 if (start->partial == 3) {
923 if (start->address & 1) {
924 context->vsram[(start->address/2) & 63] &= 0xFF;
925 context->vsram[(start->address/2) & 63] |= start->value << 8;
926 } else {
927 context->vsram[(start->address/2) & 63] &= 0xFF00;
928 context->vsram[(start->address/2) & 63] |= start->value;
929 }
930 } else {
931 context->vsram[(start->address/2) & 63] = start->partial ? context->fifo[context->fifo_write].value : start->value;
932 }
933 }
934
935 break;
936 }
937 context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1);
938 if (context->fifo_read == context->fifo_write) {
939 if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
940 context->flags |= FLAG_DMA_RUN;
941 }
942 context->fifo_read = -1;
943 }
944 } else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_COPY) {
945 if (context->flags & FLAG_READ_FETCHED) {
946 write_vram_byte(context, context->address ^ 1, context->prefetch);
947
948 //Update DMA state
949 vdp_advance_dma(context);
950
951 context->flags &= ~FLAG_READ_FETCHED;
952 } else {
953 context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1];
954
955 context->flags |= FLAG_READ_FETCHED;
956 }
957 } else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING))) {
958 switch(context->cd & 0xF)
959 {
960 case VRAM_READ:
961 if (context->flags2 & FLAG2_READ_PENDING) {
962 context->prefetch |= context->vdpmem[context->address | 1];
963 context->flags |= FLAG_READ_FETCHED;
964 context->flags2 &= ~FLAG2_READ_PENDING;
965 //Should this happen after the prefetch or after the read?
966 increment_address(context);
967 } else {
968 //TODO: 128K VRAM Mode
969 context->prefetch = context->vdpmem[context->address & 0xFFFE] << 8;
970 context->flags2 |= FLAG2_READ_PENDING;
971 }
972 break;
973 case VRAM_READ8: {
974 uint32_t address = context->address ^ 1;
975 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
976 address = mode4_address_map[address & 0x3FFF];
977 }
978 context->prefetch = context->vdpmem[address];
979 context->prefetch |= context->fifo[context->fifo_write].value & 0xFF00;
980 context->flags |= FLAG_READ_FETCHED;
981 //Should this happen after the prefetch or after the read?
982 increment_address(context);
983 break;
984 }
985 case CRAM_READ:
986 context->prefetch = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS;
987 context->prefetch |= context->fifo[context->fifo_write].value & ~CRAM_BITS;
988 context->flags |= FLAG_READ_FETCHED;
989 //Should this happen after the prefetch or after the read?
990 increment_address(context);
991 break;
992 case VSRAM_READ: {
993 uint16_t address = (context->address /2) & 63;
994 if (address >= VSRAM_SIZE) {
995 address = 0;
996 }
997 context->prefetch = context->vsram[address] & VSRAM_BITS;
998 context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS;
999 context->flags |= FLAG_READ_FETCHED;
1000 //Should this happen after the prefetch or after the read?
1001 increment_address(context);
1002 break;
1003 }
1004 }
1005 }
1006}
1007
1008static void run_dma_src(vdp_context * context, int32_t slot)
1009{
1010 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode
1011 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations
1012 if (context->fifo_write == context->fifo_read) {
1013 return;
1014 }
1015 fifo_entry * cur = NULL;
1016 if (!(context->regs[REG_DMASRC_H] & 0x80))
1017 {
1018 //68K -> VDP
1019 if (slot == -1 || !is_refresh(context, slot-1)) {
1020 cur = context->fifo + context->fifo_write;
1021 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
1022 cur->address = context->address;
1023 cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
1024 cur->cd = context->cd;
1025 cur->partial = 0;
1026 if (context->fifo_read < 0) {
1027 context->fifo_read = context->fifo_write;
1028 }
1029 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
1030 vdp_advance_dma(context);
1031 }
1032 }
1033}
1034
1035#define WINDOW_RIGHT 0x80
1036#define WINDOW_DOWN 0x80
1037
1038static void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_t address, uint16_t hscroll_val, vdp_context * context)
1039{
1040 uint16_t window_line_shift, v_offset_mask, vscroll_shift;
1041 if (context->double_res) {
1042 line *= 2;
1043 if (context->flags2 & FLAG2_EVEN_FIELD) {
1044 line++;
1045 }
1046 window_line_shift = 4;
1047 v_offset_mask = 0xF;
1048 vscroll_shift = 4;
1049 } else {
1050 window_line_shift = 3;
1051 v_offset_mask = 0x7;
1052 vscroll_shift = 3;
1053 }
1054 //TODO: Further research on vscroll latch behavior and the "first column bug"
1055 if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
1056 if (!column) {
1057 if (context->regs[REG_MODE_4] & BIT_H40) {
1058 //Based on observed behavior documented by Eke-Eke, I'm guessing the VDP
1059 //ends up fetching the last value on the VSRAM bus in the H40 case
1060 //getting the last latched value should be close enough for now
1061 if (!vsram_off) {
1062 context->vscroll_latch[0] = context->vscroll_latch[1];
1063 }
1064 } else {
1065 //supposedly it's always forced to 0 in the H32 case
1066 context->vscroll_latch[0] = context->vscroll_latch[1] = 0;
1067 }
1068 } else if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
1069 context->vscroll_latch[vsram_off] = context->vsram[column - 2 + vsram_off];
1070 }
1071 }
1072 if (!vsram_off) {
1073 uint16_t left_col, right_col;
1074 if (context->regs[REG_WINDOW_H] & WINDOW_RIGHT) {
1075 left_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2 + 2;
1076 right_col = 42;
1077 } else {
1078 left_col = 0;
1079 right_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2;
1080 if (right_col) {
1081 right_col += 2;
1082 }
1083 }
1084 uint16_t top_line, bottom_line;
1085 if (context->regs[REG_WINDOW_V] & WINDOW_DOWN) {
1086 top_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
1087 bottom_line = context->double_res ? 481 : 241;
1088 } else {
1089 top_line = 0;
1090 bottom_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
1091 }
1092 if ((column >= left_col && column < right_col) || (line >= top_line && line < bottom_line)) {
1093 uint16_t address = context->regs[REG_WINDOW] << 10;
1094 uint16_t line_offset, offset, mask;
1095 if (context->regs[REG_MODE_4] & BIT_H40) {
1096 address &= 0xF000;
1097 line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF;
1098 mask = 0x7F;
1099
1100 } else {
1101 address &= 0xF800;
1102 line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF;
1103 mask = 0x3F;
1104 }
1105 if (context->double_res) {
1106 mask <<= 1;
1107 mask |= 1;
1108 }
1109 offset = address + line_offset + (((column - 2) * 2) & mask);
1110 context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
1111 //printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
1112 offset = address + line_offset + (((column - 1) * 2) & mask);
1113 context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
1114 context->v_offset = (line) & v_offset_mask;
1115 context->flags |= FLAG_WINDOW;
1116 return;
1117 }
1118 context->flags &= ~FLAG_WINDOW;
1119 }
1120 //TODO: Verify behavior for 0x20 case
1121 uint16_t vscroll = 0xFF | (context->regs[REG_SCROLL] & 0x30) << 4;
1122 if (context->double_res) {
1123 vscroll <<= 1;
1124 vscroll |= 1;
1125 }
1126 vscroll &= context->vscroll_latch[vsram_off] + line;
1127 context->v_offset = vscroll & v_offset_mask;
1128 //printf("%s | line %d, vsram: %d, vscroll: %d, v_offset: %d\n",(vsram_off ? "B" : "A"), line, context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0], vscroll, context->v_offset);
1129 vscroll >>= vscroll_shift;
1130 uint16_t hscroll_mask;
1131 uint16_t v_mul;
1132 switch(context->regs[REG_SCROLL] & 0x3)
1133 {
1134 case 0:
1135 hscroll_mask = 0x1F;
1136 v_mul = 64;
1137 break;
1138 case 0x1:
1139 hscroll_mask = 0x3F;
1140 v_mul = 128;
1141 break;
1142 case 0x2:
1143 //TODO: Verify this behavior
1144 hscroll_mask = 0x1F;
1145 v_mul = 0;
1146 break;
1147 case 0x3:
1148 hscroll_mask = 0x7F;
1149 v_mul = 256;
1150 break;
1151 }
1152 uint16_t hscroll, offset;
1153 for (int i = 0; i < 2; i++) {
1154 hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask;
1155 offset = address + ((vscroll * v_mul + hscroll*2) & 0x1FFF);
1156 //printf("%s | line: %d, col: %d, x: %d, hs_mask %X, scr reg: %X, tbl addr: %X\n", (vsram_off ? "B" : "A"), line, (column-2+i), hscroll, hscroll_mask, context->regs[REG_SCROLL], offset);
1157 uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
1158 if (i) {
1159 context->col_2 = col_val;
1160 } else {
1161 context->col_1 = col_val;
1162 }
1163 }
1164}
1165
1166static void read_map_scroll_a(uint16_t column, uint32_t line, vdp_context * context)
1167{
1168 read_map_scroll(column, 0, line, (context->regs[REG_SCROLL_A] & 0x38) << 10, context->hscroll_a, context);
1169}
1170
1171static void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context)
1172{
1173 read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context);
1174}
1175
1176static void read_map_mode4(uint16_t column, uint32_t line, vdp_context * context)
1177{
1178 uint32_t address = (context->regs[REG_SCROLL_A] & 0xE) << 10;
1179 //add row
1180 uint32_t vscroll = line;
1181 if (column < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) {
1182 vscroll += context->regs[REG_Y_SCROLL];
1183 }
1184 if (vscroll > 223) {
1185 vscroll -= 224;
1186 }
1187 address += (vscroll >> 3) * 2 * 32;
1188 //add column
1189 address += ((column - (context->hscroll_a >> 3)) & 31) * 2;
1190 //adjust for weird VRAM mapping in Mode 4
1191 address = mode4_address_map[address];
1192 context->col_1 = (context->vdpmem[address] << 8) | context->vdpmem[address+1];
1193}
1194
1195static void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context)
1196{
1197 uint16_t address;
1198 uint16_t vflip_base;
1199 if (context->double_res) {
1200 address = ((col & 0x3FF) << 6);
1201 vflip_base = 60;
1202 } else {
1203 address = ((col & 0x7FF) << 5);
1204 vflip_base = 28;
1205 }
1206 if (col & MAP_BIT_V_FLIP) {
1207 address += vflip_base - 4 * context->v_offset;
1208 } else {
1209 address += 4 * context->v_offset;
1210 }
1211 uint8_t pal_priority = (col >> 9) & 0x70;
1212 uint32_t bits = *((uint32_t *)(&context->vdpmem[address]));
1213 if (col & MAP_BIT_H_FLIP) {
1214 uint32_t shift = 28;
1215 for (int i = 0; i < 4; i++)
1216 {
1217 uint8_t right = pal_priority | ((bits >> shift) & 0xF);
1218 shift -= 4;
1219 tmp_buf[offset++] = pal_priority | ((bits >> shift) & 0xF);
1220 shift -= 4;
1221 offset &= SCROLL_BUFFER_MASK;
1222 tmp_buf[offset++] = right;
1223 offset &= SCROLL_BUFFER_MASK;
1224 }
1225 } else {
1226 for (int i = 0; i < 4; i++)
1227 {
1228 uint8_t right = pal_priority | (bits & 0xF);
1229 bits >>= 4;
1230 tmp_buf[offset++] = pal_priority | (bits & 0xF);
1231 offset &= SCROLL_BUFFER_MASK;
1232 bits >>= 4;
1233 tmp_buf[offset++] = right;
1234 offset &= SCROLL_BUFFER_MASK;
1235 }
1236 }
1237}
1238
1239static void render_map_1(vdp_context * context)
1240{
1241 render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context);
1242}
1243
1244static void render_map_2(vdp_context * context)
1245{
1246 render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context);
1247}
1248
1249static void render_map_3(vdp_context * context)
1250{
1251 render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context);
1252}
1253
1254static void fetch_map_mode4(uint16_t col, uint32_t line, vdp_context *context)
1255{
1256 //calculate pixel row to fetch
1257 uint32_t vscroll = line;
1258 if (col < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) {
1259 vscroll += context->regs[REG_Y_SCROLL];
1260 }
1261 if (vscroll > 223) {
1262 vscroll -= 224;
1263 }
1264 vscroll &= 7;
1265 if (context->col_1 & 0x400) {
1266 vscroll = 7 - vscroll;
1267 }
1268
1269 uint32_t address = mode4_address_map[((context->col_1 & 0x1FF) * 32) + vscroll * 4];
1270 context->fetch_tmp[0] = context->vdpmem[address];
1271 context->fetch_tmp[1] = context->vdpmem[address+1];
1272}
1273
1274static uint8_t composite_normal(vdp_context *context, uint8_t *debug_dst, uint8_t sprite, uint8_t plane_a, uint8_t plane_b, uint8_t bg_index)
1275{
1276 uint8_t pixel = bg_index;
1277 uint8_t src = DBG_SRC_BG;
1278 if (plane_b & 0xF) {
1279 pixel = plane_b;
1280 src = DBG_SRC_B;
1281 }
1282 if (plane_a & 0xF && (plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1283 pixel = plane_a;
1284 src = DBG_SRC_A;
1285 }
1286 if (sprite & 0xF && (sprite & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1287 pixel = sprite;
1288 src = DBG_SRC_S;
1289 }
1290 *debug_dst = src;
1291 return pixel;
1292}
1293typedef struct {
1294 uint8_t index, intensity;
1295} sh_pixel;
1296
1297static sh_pixel composite_highlight(vdp_context *context, uint8_t *debug_dst, uint8_t sprite, uint8_t plane_a, uint8_t plane_b, uint8_t bg_index)
1298{
1299 uint8_t pixel = bg_index;
1300 uint8_t src = DBG_SRC_BG;
1301 uint8_t intensity = 0;
1302 if (plane_b & 0xF) {
1303 pixel = plane_b;
1304 src = DBG_SRC_B;
1305 }
1306 intensity = plane_b & BUF_BIT_PRIORITY;
1307 if (plane_a & 0xF && (plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1308 pixel = plane_a;
1309 src = DBG_SRC_A;
1310 }
1311 intensity |= plane_a & BUF_BIT_PRIORITY;
1312 if (sprite & 0xF && (sprite & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
1313 if ((sprite & 0x3F) == 0x3E) {
1314 intensity += BUF_BIT_PRIORITY;
1315 } else if ((sprite & 0x3F) == 0x3F) {
1316 intensity = 0;
1317 } else {
1318 pixel = sprite;
1319 src = DBG_SRC_S;
1320 if ((pixel & 0xF) == 0xE) {
1321 intensity = BUF_BIT_PRIORITY;
1322 } else {
1323 intensity |= pixel & BUF_BIT_PRIORITY;
1324 }
1325 }
1326 }
1327 *debug_dst = src;
1328 return (sh_pixel){.index = pixel, .intensity = intensity};
1329}
1330
1331static void render_normal(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off)
1332{
1333 int start = 0;
1334 if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1335 uint32_t bgcolor = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
1336 for (int i = 0; i < 8; ++i)
1337 {
1338 *(dst++) = bgcolor;
1339 *(debug_dst++) = DBG_SRC_BG;
1340 }
1341 start = 8;
1342 }
1343 uint8_t *sprite_buf = context->linebuf + col * 8 + start;
1344 for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1345 {
1346 uint8_t sprite, plane_a, plane_b;
1347 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1348 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1349 sprite = *sprite_buf;
1350 uint8_t pixel = composite_normal(context, debug_dst, sprite, plane_a, plane_b, context->regs[REG_BG_COLOR]);
1351 debug_dst++;
1352 *(dst++) = context->colors[pixel & 0x3F];
1353 }
1354}
1355
1356static void render_highlight(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off)
1357{
1358 int start = 0;
1359 if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1360 uint32_t bgcolor = context->colors[SHADOW_OFFSET + (context->regs[REG_BG_COLOR] & 0x3F)];
1361 for (int i = 0; i < 8; ++i)
1362 {
1363 *(dst++) = bgcolor;
1364 *(debug_dst++) = DBG_SRC_BG | DBG_SHADOW;
1365 }
1366 start = 8;
1367 }
1368 uint8_t *sprite_buf = context->linebuf + col * 8 + start;
1369 for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1370 {
1371 uint8_t sprite, plane_a, plane_b;
1372 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1373 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1374 sprite = *sprite_buf;
1375 sh_pixel pixel = composite_highlight(context, debug_dst, sprite, plane_a, plane_b, context->regs[REG_BG_COLOR]);
1376 uint32_t *colors;
1377 if (pixel.intensity == BUF_BIT_PRIORITY << 1) {
1378 colors = context->colors + HIGHLIGHT_OFFSET;
1379 } else if (pixel.intensity) {
1380 colors = context->colors;
1381 } else {
1382 colors = context->colors + SHADOW_OFFSET;
1383 }
1384 debug_dst++;
1385 *(dst++) = colors[pixel.index & 0x3F];
1386 }
1387}
1388
1389static void render_testreg(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off, uint8_t output_disabled, uint8_t test_layer)
1390{
1391 if (output_disabled) {
1392 switch (test_layer)
1393 {
1394 case 0:
1395 for (int i = 0; i < 16; i++)
1396 {
1397 *(dst++) = 0x3F; //TODO: confirm this on hardware
1398 *(debug_dst++) = DBG_SRC_BG;
1399 }
1400 break;
1401 case 1: {
1402 uint8_t *sprite_buf = context->linebuf + col * 8;
1403 for (int i = 0; i < 16; i++)
1404 {
1405 *(dst++) = context->colors[*(sprite_buf++) & 0x3F];
1406 *(debug_dst++) = DBG_SRC_S;
1407 }
1408 break;
1409 }
1410 case 2:
1411 for (int i = 0; i < 16; i++)
1412 {
1413 *(dst++) = context->colors[context->tmp_buf_a[(plane_a_off++) & SCROLL_BUFFER_MASK] & 0x3F];
1414 *(debug_dst++) = DBG_SRC_A;
1415 }
1416 break;
1417 case 3:
1418 for (int i = 0; i < 16; i++)
1419 {
1420 *(dst++) = context->colors[context->tmp_buf_b[(plane_b_off++) & SCROLL_BUFFER_MASK] & 0x3F];
1421 *(debug_dst++) = DBG_SRC_B;
1422 }
1423 break;
1424 }
1425 } else {
1426 int start = 0;
1427 uint8_t *sprite_buf = context->linebuf + col * 8;
1428 if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1429 //TODO: Confirm how test register interacts with column 0 blanking
1430 uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
1431 uint8_t src = DBG_SRC_BG;
1432 for (int i = 0; i < 8; ++i)
1433 {
1434 switch (test_layer)
1435 {
1436 case 1:
1437 pixel &= sprite_buf[i];
1438 if (pixel) {
1439 src = DBG_SRC_S;
1440 }
1441 break;
1442 case 2:
1443 pixel &= context->tmp_buf_a[(plane_a_off + i) & SCROLL_BUFFER_MASK];
1444 if (pixel) {
1445 src = DBG_SRC_A;
1446 }
1447 break;
1448 case 3:
1449 pixel &= context->tmp_buf_b[(plane_b_off + i) & SCROLL_BUFFER_MASK];
1450 if (pixel) {
1451 src = DBG_SRC_B;
1452 }
1453 break;
1454 }
1455
1456 *(dst++) = context->colors[pixel & 0x3F];
1457 *(debug_dst++) = src;
1458 }
1459 plane_a_off += 8;
1460 plane_b_off += 8;
1461 sprite_buf += 8;
1462 start = 8;
1463 }
1464 for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1465 {
1466 uint8_t sprite, plane_a, plane_b;
1467 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1468 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1469 sprite = *sprite_buf;
1470 uint8_t pixel = composite_normal(context, debug_dst, sprite, plane_a, plane_b, 0x3F);
1471 switch (test_layer)
1472 {
1473 case 1:
1474 pixel &= sprite;
1475 if (pixel) {
1476 *debug_dst = DBG_SRC_S;
1477 }
1478 break;
1479 case 2:
1480 pixel &= plane_a;
1481 if (pixel) {
1482 *debug_dst = DBG_SRC_A;
1483 }
1484 break;
1485 case 3:
1486 pixel &= plane_b;
1487 if (pixel) {
1488 *debug_dst = DBG_SRC_B;
1489 }
1490 break;
1491 }
1492 debug_dst++;
1493 *(dst++) = context->colors[pixel & 0x3F];
1494 }
1495 }
1496}
1497
1498static void render_testreg_highlight(vdp_context *context, int32_t col, uint32_t *dst, uint8_t *debug_dst, int plane_a_off, int plane_b_off, uint8_t output_disabled, uint8_t test_layer)
1499{
1500 int start = 0;
1501 uint8_t *sprite_buf = context->linebuf + col * 8;
1502 if (!col && (context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1503 //TODO: Confirm how test register interacts with column 0 blanking
1504 uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
1505 uint8_t src = DBG_SRC_BG | DBG_SHADOW;
1506 for (int i = 0; i < 8; ++i)
1507 {
1508 switch (test_layer)
1509 {
1510 case 1:
1511 pixel &= sprite_buf[i];
1512 if (pixel) {
1513 src = DBG_SRC_S | DBG_SHADOW;
1514 }
1515 break;
1516 case 2:
1517 pixel &= context->tmp_buf_a[(plane_a_off + i) & SCROLL_BUFFER_MASK];
1518 if (pixel) {
1519 src = DBG_SRC_A | DBG_SHADOW;
1520 }
1521 break;
1522 case 3:
1523 pixel &= context->tmp_buf_b[(plane_b_off + i) & SCROLL_BUFFER_MASK];
1524 if (pixel) {
1525 src = DBG_SRC_B | DBG_SHADOW;
1526 }
1527 break;
1528 }
1529
1530 *(dst++) = context->colors[SHADOW_OFFSET + (pixel & 0x3F)];
1531 *(debug_dst++) = src;
1532 }
1533 plane_a_off += 8;
1534 plane_b_off += 8;
1535 sprite_buf += 8;
1536 start = 8;
1537 }
1538 for (int i = start; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i)
1539 {
1540 uint8_t sprite, plane_a, plane_b;
1541 plane_a = context->tmp_buf_a[plane_a_off & SCROLL_BUFFER_MASK];
1542 plane_b = context->tmp_buf_b[plane_b_off & SCROLL_BUFFER_MASK];
1543 sprite = *sprite_buf;
1544 sh_pixel pixel = composite_highlight(context, debug_dst, sprite, plane_a, plane_b, 0x3F);
1545 uint32_t *colors;
1546 if (pixel.intensity == BUF_BIT_PRIORITY << 1) {
1547 colors = context->colors + HIGHLIGHT_OFFSET;
1548 } else if (pixel.intensity) {
1549 colors = context->colors;
1550 } else {
1551 colors = context->colors + SHADOW_OFFSET;
1552 }
1553 if (output_disabled) {
1554 pixel.index = 0x3F;
1555 }
1556 switch (test_layer)
1557 {
1558 case 1:
1559 pixel.index &= sprite;
1560 if (pixel.index) {
1561 *debug_dst = DBG_SRC_S;
1562 }
1563 break;
1564 case 2:
1565 pixel.index &= plane_a;
1566 if (pixel.index) {
1567 *debug_dst = DBG_SRC_A;
1568 }
1569 break;
1570 case 3:
1571 pixel.index &= plane_b;
1572 if (pixel.index) {
1573 *debug_dst = DBG_SRC_B;
1574 }
1575 break;
1576 }
1577 debug_dst++;
1578 *(dst++) = colors[pixel.index & 0x3F];
1579 }
1580}
1581
1582static void render_map_output(uint32_t line, int32_t col, vdp_context * context)
1583{
1584 uint32_t *dst;
1585 uint8_t *debug_dst;
1586 uint8_t output_disabled = (context->test_port & TEST_BIT_DISABLE) != 0;
1587 uint8_t test_layer = context->test_port >> 7 & 3;
1588 if (context->state == PREPARING && !test_layer) {
1589 if (col) {
1590 col -= 2;
1591 dst = context->output + BORDER_LEFT + col * 8;
1592 } else {
1593 dst = context->output;
1594 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
1595 for (int i = 0; i < BORDER_LEFT; i++, dst++)
1596 {
1597 *dst = bg_color;
1598 }
1599 context->done_output = dst;
1600 return;
1601 }
1602 uint32_t color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
1603 for (int i = 0; i < 16; i++)
1604 {
1605 *(dst++) = color;
1606 }
1607 context->done_output = dst;
1608 return;
1609 }
1610 line &= 0xFF;
1611 render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context);
1612 uint8_t *sprite_buf;
1613 uint8_t sprite, plane_a, plane_b;
1614 int plane_a_off, plane_b_off;
1615 if (col)
1616 {
1617 col-=2;
1618 dst = context->output + BORDER_LEFT + col * 8;
1619 debug_dst = context->layer_debug_buf + BORDER_LEFT + col * 8;
1620
1621
1622 uint8_t a_src, src;
1623 if (context->flags & FLAG_WINDOW) {
1624 plane_a_off = context->buf_a_off;
1625 a_src = DBG_SRC_W;
1626 } else {
1627 plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF);
1628 a_src = DBG_SRC_A;
1629 }
1630 plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
1631 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
1632
1633 if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
1634 if (output_disabled || test_layer) {
1635 render_testreg_highlight(context, col, dst, debug_dst, plane_a_off, plane_b_off, output_disabled, test_layer);
1636 } else {
1637 render_highlight(context, col, dst, debug_dst, plane_a_off, plane_b_off);
1638 }
1639 } else {
1640 if (output_disabled || test_layer) {
1641 render_testreg(context, col, dst, debug_dst, plane_a_off, plane_b_off, output_disabled, test_layer);
1642 } else {
1643 render_normal(context, col, dst, debug_dst, plane_a_off, plane_b_off);
1644 }
1645 }
1646 dst += 16;
1647 } else {
1648 dst = context->output;
1649 debug_dst = context->layer_debug_buf;
1650 uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
1651 if (output_disabled) {
1652 pixel = 0x3F;
1653 }
1654 uint32_t bg_color = context->colors[pixel];
1655 if (test_layer) {
1656 switch(test_layer)
1657 {
1658 case 1:
1659 bg_color = context->colors[0];
1660 for (int i = 0; i < BORDER_LEFT; i++, dst++, debug_dst++)
1661 {
1662 *dst = bg_color;
1663 *debug_dst = DBG_SRC_BG;
1664
1665 }
1666 break;
1667 case 2: {
1668 //plane A
1669 //TODO: Deal with Window layer
1670 int i;
1671 i = 0;
1672 uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF) + (16 - BORDER_LEFT);
1673 //uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK);
1674 for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++)
1675 {
1676 *dst = context->colors[context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK]];
1677 *debug_dst = DBG_SRC_A;
1678 }
1679 break;
1680 }
1681 case 3: {
1682 //plane B
1683 int i;
1684 i = 0;
1685 uint8_t buf_off = context->buf_b_off - (context->hscroll_b & 0xF) + (16 - BORDER_LEFT);
1686 //uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK);
1687 for (; i < BORDER_LEFT; buf_off++, i++, dst++, debug_dst++)
1688 {
1689 *dst = context->colors[context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK]];
1690 *debug_dst = DBG_SRC_B;
1691 }
1692 break;
1693 }
1694 }
1695 } else {
1696 for (int i = 0; i < BORDER_LEFT; i++, dst++, debug_dst++)
1697 {
1698 *dst = bg_color;
1699 *debug_dst = DBG_SRC_BG;
1700 }
1701 }
1702 }
1703 context->done_output = dst;
1704 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
1705 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
1706}
1707
1708static void render_map_mode4(uint32_t line, int32_t col, vdp_context * context)
1709{
1710 uint32_t vscroll = line;
1711 if (col < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) {
1712 vscroll += context->regs[REG_Y_SCROLL];
1713 }
1714 if (vscroll > 223) {
1715 vscroll -= 224;
1716 }
1717 vscroll &= 7;
1718 if (context->col_1 & 0x400) {
1719 //vflip
1720 vscroll = 7 - vscroll;
1721 }
1722
1723 uint32_t pixels = planar_to_chunky[context->fetch_tmp[0]] << 1;
1724 pixels |= planar_to_chunky[context->fetch_tmp[1]];
1725
1726 uint32_t address = mode4_address_map[((context->col_1 & 0x1FF) * 32) + vscroll * 4 + 2];
1727 pixels |= planar_to_chunky[context->vdpmem[address]] << 3;
1728 pixels |= planar_to_chunky[context->vdpmem[address+1]] << 2;
1729
1730 int i, i_inc, i_limit;
1731 if (context->col_1 & 0x200) {
1732 //hflip
1733 i = 0;
1734 i_inc = 4;
1735 i_limit = 32;
1736 } else {
1737 i = 28;
1738 i_inc = -4;
1739 i_limit = -4;
1740 }
1741 uint8_t pal_priority = (context->col_1 >> 7 & 0x10) | (context->col_1 >> 6 & 0x40);
1742 for (uint8_t *dst = context->tmp_buf_a + context->buf_a_off; i != i_limit; i += i_inc, dst++)
1743 {
1744 *dst = (pixels >> i & 0xF) | pal_priority;
1745 }
1746 context->buf_a_off = (context->buf_a_off + 8) & 15;
1747
1748 uint8_t bgcolor = 0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET;
1749 uint32_t *dst = context->output + col * 8 + BORDER_LEFT;
1750 uint8_t *debug_dst = context->layer_debug_buf + col * 8 + BORDER_LEFT;
1751 if (context->state == PREPARING) {
1752 for (int i = 0; i < 16; i++)
1753 {
1754 *(dst++) = context->colors[bgcolor];
1755 }
1756 context->done_output = dst;
1757 return;
1758 }
1759
1760 if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK)) {
1761 uint8_t *sprite_src = context->linebuf + col * 8;
1762 if (context->regs[REG_MODE_1] & BIT_SPRITE_8PX) {
1763 sprite_src += 8;
1764 }
1765 for (int i = 0; i < 8; i++, sprite_src++)
1766 {
1767 uint8_t *bg_src = context->tmp_buf_a + ((8 + i + col * 8 - (context->hscroll_a & 0x7)) & 15);
1768 if ((*bg_src & 0x4F) > 0x40 || !*sprite_src) {
1769 //background plane has priority and is opaque or sprite layer is transparent
1770 *(dst++) = context->colors[(*bg_src & 0x1F) + MODE4_OFFSET];
1771 *(debug_dst++) = DBG_SRC_A;
1772 } else {
1773 //sprite layer is opaque and not covered by high priority BG pixels
1774 *(dst++) = context->colors[*sprite_src | 0x10 + MODE4_OFFSET];
1775 *(debug_dst++) = DBG_SRC_S;
1776 }
1777 }
1778 } else {
1779 for (int i = 0; i < 8; i++)
1780 {
1781 *(dst++) = context->colors[bgcolor];
1782 *(debug_dst++) = DBG_SRC_BG;
1783 }
1784 }
1785 context->done_output = dst;
1786}
1787
1788static uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19};
1789
1790static void vdp_advance_line(vdp_context *context)
1791{
1792#ifdef TIMING_DEBUG
1793 static uint32_t last_line = 0xFFFFFFFF;
1794 if (last_line != 0xFFFFFFFF) {
1795 uint32_t diff = context->cycles - last_line;
1796 if (diff != MCLKS_LINE) {
1797 printf("Line %d took %d cycles\n", context->vcounter, diff);
1798 }
1799 }
1800 last_line = context->cycles;
1801#endif
1802 uint16_t jump_start, jump_end;
1803 uint8_t is_mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
1804 if (is_mode_5) {
1805 if (context->flags2 & FLAG2_REGION_PAL) {
1806 if (context->regs[REG_MODE_2] & BIT_PAL) {
1807 jump_start = 0x10B;
1808 jump_end = 0x1D2;
1809 } else {
1810 jump_start = 0x103;
1811 jump_end = 0x1CA;
1812 }
1813 } else if (context->regs[REG_MODE_2] & BIT_PAL) {
1814 jump_start = 0x100;
1815 jump_end = 0x1FA;
1816 } else {
1817 jump_start = 0xEB;
1818 jump_end = 0x1E5;
1819 }
1820 } else {
1821 jump_start = 0xDB;
1822 jump_end = 0x1D5;
1823 }
1824
1825 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM | 1 << VDP_DEBUG_COMPOSITE)) {
1826 uint32_t line = context->vcounter;
1827 if (line >= jump_end) {
1828 line -= jump_end - jump_start;
1829 }
1830 uint32_t total_lines = (context->flags2 & FLAG2_REGION_PAL) ? 313 : 262;
1831
1832 if (total_lines - line <= context->border_top) {
1833 line -= total_lines - context->border_top;
1834 } else {
1835 line += context->border_top;
1836 }
1837 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) {
1838 uint32_t *fb = context->debug_fbs[VDP_DEBUG_CRAM] + context->debug_fb_pitch[VDP_DEBUG_CRAM] * line / sizeof(uint32_t);
1839 for (int i = 0; i < 64; i++)
1840 {
1841 for (int x = 0; x < 8; x++)
1842 {
1843 *(fb++) = context->colors[i];
1844 }
1845 }
1846 }
1847 if (
1848 context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)
1849 && line < (context->inactive_start + context->border_bot + context->border_top)
1850 ) {
1851 uint32_t *fb = context->debug_fbs[VDP_DEBUG_COMPOSITE] + context->debug_fb_pitch[VDP_DEBUG_COMPOSITE] * line / sizeof(uint32_t);
1852 for (int i = 0; i < LINEBUF_SIZE; i++)
1853 {
1854 *(fb++) = context->debugcolors[context->layer_debug_buf[i]];
1855 }
1856 }
1857 }
1858
1859 context->vcounter++;
1860 if (context->vcounter == jump_start) {
1861 context->vcounter = jump_end;
1862 } else {
1863 context->vcounter &= 0x1FF;
1864 }
1865 if (context->state == PREPARING) {
1866 context->state = ACTIVE;
1867 }
1868 if (context->vcounter == 0x1FF) {
1869 context->flags2 &= ~FLAG2_PAUSE;
1870 }
1871
1872 if (context->state != ACTIVE) {
1873 context->hint_counter = context->regs[REG_HINT];
1874 } else if (context->hint_counter) {
1875 context->hint_counter--;
1876 } else {
1877 context->flags2 |= FLAG2_HINT_PENDING;
1878 context->pending_hint_start = context->cycles;
1879 context->hint_counter = context->regs[REG_HINT];
1880 }
1881}
1882
1883static void vdp_update_per_frame_debug(vdp_context *context)
1884{
1885 if (context->enabled_debuggers & (1 << VDP_DEBUG_PLANE)) {
1886 uint32_t pitch;
1887 uint32_t *fb = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_PLANE], &pitch);
1888 uint16_t hscroll_mask;
1889 uint16_t v_mul;
1890 uint16_t vscroll_mask = 0x1F | (context->regs[REG_SCROLL] & 0x30) << 1;
1891 switch(context->regs[REG_SCROLL] & 0x3)
1892 {
1893 case 0:
1894 hscroll_mask = 0x1F;
1895 v_mul = 64;
1896 break;
1897 case 0x1:
1898 hscroll_mask = 0x3F;
1899 v_mul = 128;
1900 break;
1901 case 0x2:
1902 //TODO: Verify this behavior
1903 hscroll_mask = 0x1F;
1904 v_mul = 0;
1905 break;
1906 case 0x3:
1907 hscroll_mask = 0x7F;
1908 v_mul = 256;
1909 break;
1910 }
1911 uint16_t table_address;
1912 switch(context->debug_modes[VDP_DEBUG_PLANE] % 3)
1913 {
1914 case 0:
1915 table_address = context->regs[REG_SCROLL_A] << 10 & 0xE000;
1916 break;
1917 case 1:
1918 table_address = context->regs[REG_SCROLL_B] << 13 & 0xE000;
1919 break;
1920 case 2:
1921 table_address = context->regs[REG_WINDOW] << 10;
1922 if (context->regs[REG_MODE_4] & BIT_H40) {
1923 table_address &= 0xF000;
1924 v_mul = 128;
1925 hscroll_mask = 0x3F;
1926 } else {
1927 table_address &= 0xF800;
1928 v_mul = 64;
1929 hscroll_mask = 0x1F;
1930 }
1931 vscroll_mask = 0x1F;
1932 break;
1933 }
1934 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR & 0x3F]];
1935 for (uint16_t row = 0; row < 128; row++)
1936 {
1937 uint16_t row_address = table_address + (row & vscroll_mask) * v_mul;
1938 for (uint16_t col = 0; col < 128; col++)
1939 {
1940 uint16_t address = row_address + (col & hscroll_mask) * 2;
1941 //pccv hnnn nnnn nnnn
1942 //
1943 uint16_t entry = context->vdpmem[address] << 8 | context->vdpmem[address + 1];
1944 uint8_t pal = entry >> 9 & 0x30;
1945
1946 uint32_t *dst = fb + (row * pitch * 8 / sizeof(uint32_t)) + col * 8;
1947 address = (entry & 0x7FF) * 32;
1948 int y_diff = 4;
1949 if (entry & 0x1000) {
1950 y_diff = -4;
1951 address += 7 * 4;
1952 }
1953 int x_diff = 1;
1954 if (entry & 0x800) {
1955 x_diff = -1;
1956 address += 3;
1957 }
1958 for (int y = 0; y < 8; y++)
1959 {
1960 uint16_t trow_address = address;
1961 uint32_t *row_dst = dst;
1962 for (int x = 0; x < 4; x++)
1963 {
1964 uint8_t byte = context->vdpmem[trow_address];
1965 trow_address += x_diff;
1966 uint8_t left, right;
1967 if (x_diff > 0) {
1968 left = byte >> 4;
1969 right = byte & 0xF;
1970 } else {
1971 left = byte & 0xF;
1972 right = byte >> 4;
1973 }
1974 *(row_dst++) = left ? context->colors[left|pal] : bg_color;
1975 *(row_dst++) = right ? context->colors[right|pal] : bg_color;
1976 }
1977 address += y_diff;
1978 dst += pitch / sizeof(uint32_t);
1979 }
1980 }
1981 }
1982 render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_PLANE], 1024);
1983 }
1984
1985 if (context->enabled_debuggers & (1 << VDP_DEBUG_VRAM)) {
1986 uint32_t pitch;
1987 uint32_t *fb = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_VRAM], &pitch);
1988
1989 uint8_t pal = (context->debug_modes[VDP_DEBUG_VRAM] % 4) << 4;
1990 for (int y = 0; y < 512; y++)
1991 {
1992 uint32_t *line = fb + y * pitch / sizeof(uint32_t);
1993 int row = y >> 4;
1994 int yoff = y >> 1 & 7;
1995 for (int col = 0; col < 64; col++)
1996 {
1997 uint16_t address = (row * 64 + col) * 32 + yoff * 4;
1998 for (int x = 0; x < 4; x++)
1999 {
2000 uint8_t byte = context->vdpmem[address++];
2001 uint8_t left = byte >> 4 | pal;
2002 uint8_t right = byte & 0xF | pal;
2003 *(line++) = context->colors[left];
2004 *(line++) = context->colors[left];
2005 *(line++) = context->colors[right];
2006 *(line++) = context->colors[right];
2007 }
2008 }
2009 }
2010
2011 render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_VRAM], 1024);
2012 }
2013
2014 if (context->enabled_debuggers & (1 << VDP_DEBUG_CRAM)) {
2015 uint32_t starting_line = 512 - 32*4;
2016 uint32_t *line = context->debug_fbs[VDP_DEBUG_CRAM]
2017 + context->debug_fb_pitch[VDP_DEBUG_CRAM] * starting_line / sizeof(uint32_t);
2018 for (int pal = 0; pal < 4; pal ++)
2019 {
2020 uint32_t *cur;
2021 for (int y = 0; y < 31; y++)
2022 {
2023 cur = line;
2024 for (int offset = 0; offset < 16; offset++)
2025 {
2026 for (int x = 0; x < 31; x++)
2027 {
2028 *(cur++) = context->colors[pal * 16 + offset];
2029 }
2030 *(cur++) = 0xFF000000;
2031 }
2032 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t);
2033 }
2034 cur = line;
2035 for (int x = 0; x < 512; x++)
2036 {
2037 *(cur++) = 0xFF000000;
2038 }
2039 line += context->debug_fb_pitch[VDP_DEBUG_CRAM] / sizeof(uint32_t);
2040 }
2041 render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_CRAM], 512);
2042 context->debug_fbs[VDP_DEBUG_CRAM] = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_CRAM], &context->debug_fb_pitch[VDP_DEBUG_CRAM]);
2043 }
2044 if (context->enabled_debuggers & (1 << VDP_DEBUG_COMPOSITE)) {
2045 render_video_buffer_updated(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], LINEBUF_SIZE);
2046 context->debug_fbs[VDP_DEBUG_COMPOSITE] = render_get_video_buffer(context->debug_fb_indices[VDP_DEBUG_COMPOSITE], &context->debug_fb_pitch[VDP_DEBUG_COMPOSITE]);
2047 }
2048}
2049
2050void vdp_force_update_framebuffer(vdp_context *context)
2051{
2052 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL)
2053 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2054 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2055
2056 uint16_t to_fill = lines_max - context->output_lines;
2057 memset(
2058 ((char *)context->fb) + context->output_pitch * context->output_lines,
2059 0,
2060 to_fill * context->output_pitch
2061 );
2062 render_video_buffer_updated(context->cur_buffer, context->h40_lines > context->output_lines / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2063 context->fb = render_get_video_buffer(context->cur_buffer, &context->output_pitch);
2064 vdp_update_per_frame_debug(context);
2065}
2066
2067static void advance_output_line(vdp_context *context)
2068{
2069 if (headless) {
2070 if (context->vcounter == context->inactive_start) {
2071 context->frame++;
2072 }
2073 context->vcounter &= 0x1FF;
2074 } else {
2075 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL)
2076 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2077 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2078
2079 if (context->output_lines == lines_max) {
2080 render_video_buffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2081 context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? VIDEO_BUFFER_EVEN : VIDEO_BUFFER_ODD;
2082 context->fb = render_get_video_buffer(context->cur_buffer, &context->output_pitch);
2083 vdp_update_per_frame_debug(context);
2084 context->h40_lines = 0;
2085 context->frame++;
2086 context->output_lines = 0;
2087 }
2088 uint32_t output_line = context->vcounter;
2089 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
2090 //vcounter increment occurs much later in Mode 4
2091 output_line++;
2092 }
2093 if (output_line < context->inactive_start + context->border_bot && context->output_lines > 0) {
2094 output_line = context->output_lines++;//context->border_top + context->vcounter;
2095 } else if (output_line >= 0x200 - context->border_top) {
2096 if (output_line == 0x200 - context->border_top) {
2097 //We're at the top of the display, force context->output_lines to be zero to avoid
2098 //potential screen rolling if the mode is changed at an inopportune time
2099 context->output_lines = 0;
2100 }
2101 output_line = context->output_lines++;//context->vcounter - (0x200 - context->border_top);
2102 } else {
2103 output_line = INVALID_LINE;
2104 }
2105 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line);
2106 context->done_output = context->output;
2107#ifdef DEBUG_FB_FILL
2108 for (int i = 0; i < LINEBUF_SIZE; i++)
2109 {
2110 context->output[i] = 0xFFFF00FF;
2111 }
2112#endif
2113 if (output_line != INVALID_LINE && (context->regs[REG_MODE_4] & BIT_H40)) {
2114 context->h40_lines++;
2115 }
2116 }
2117}
2118
2119void vdp_release_framebuffer(vdp_context *context)
2120{
2121 render_video_buffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2122 context->output = context->fb = NULL;
2123}
2124
2125void vdp_reacquire_framebuffer(vdp_context *context)
2126{
2127 context->fb = render_get_video_buffer(context->cur_buffer, &context->output_pitch);
2128 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL)
2129 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2130 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2131 if (context->output_lines <= lines_max && context->output_lines > 0) {
2132 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1));
2133 } else {
2134 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * INVALID_LINE);
2135 }
2136}
2137
2138static void render_border_garbage(vdp_context *context, uint32_t address, uint8_t *buf, uint8_t buf_off, uint16_t col)
2139{
2140 uint8_t base = col >> 9 & 0x30;
2141 for (int i = 0; i < 4; i++, address++)
2142 {
2143 uint8_t byte = context->vdpmem[address & 0xFFFF];
2144 buf[(buf_off++) & SCROLL_BUFFER_MASK] = base | byte >> 4;
2145 buf[(buf_off++) & SCROLL_BUFFER_MASK] = base | byte & 0xF;
2146 }
2147}
2148
2149static void draw_right_border(vdp_context *context)
2150{
2151 uint32_t *dst = context->output + BORDER_LEFT + ((context->regs[REG_MODE_4] & BIT_H40) ? 320 : 256);
2152 uint8_t pixel = context->regs[REG_BG_COLOR] & 0x3F;
2153 if ((context->test_port & TEST_BIT_DISABLE) != 0) {
2154 pixel = 0x3F;
2155 }
2156 uint32_t bg_color = context->colors[pixel];
2157 uint8_t test_layer = context->test_port >> 7 & 3;
2158 if (test_layer) {
2159 switch(test_layer)
2160 {
2161 case 1:
2162 bg_color = context->colors[0];
2163 for (int i = 0; i < BORDER_RIGHT; i++, dst++)
2164 {
2165 *dst = bg_color;
2166 }
2167 break;
2168 case 2: {
2169 //plane A
2170 //TODO: Deal with Window layer
2171 int i;
2172 i = 0;
2173 uint8_t buf_off = context->buf_a_off - (context->hscroll_a & 0xF);
2174 //uint8_t *src = context->tmp_buf_a + ((context->buf_a_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_a & 0xF))) & SCROLL_BUFFER_MASK);
2175 for (; i < BORDER_RIGHT; buf_off++, i++, dst++)
2176 {
2177 *dst = context->colors[context->tmp_buf_a[buf_off & SCROLL_BUFFER_MASK] & 0x3F];
2178 }
2179 break;
2180 }
2181 case 3: {
2182 //plane B
2183 int i;
2184 i = 0;
2185 uint8_t buf_off = context->buf_b_off - (context->hscroll_b & 0xF);
2186 //uint8_t *src = context->tmp_buf_b + ((context->buf_b_off + (i ? 0 : (16 - BORDER_LEFT) - (context->hscroll_b & 0xF))) & SCROLL_BUFFER_MASK);
2187 for (; i < BORDER_RIGHT; buf_off++, i++, dst++)
2188 {
2189 *dst = context->colors[context->tmp_buf_b[buf_off & SCROLL_BUFFER_MASK] & 0x3F];
2190 }
2191 break;
2192 }
2193 }
2194 } else {
2195 for (int i = 0; i < BORDER_RIGHT; i++, dst++)
2196 {
2197 *dst = bg_color;
2198 }
2199 }
2200 context->done_output = dst;
2201 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
2202 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
2203}
2204
2205#define CHECK_ONLY if (context->cycles >= target_cycles) { return; }
2206#define CHECK_LIMIT if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } context->hslot++; context->cycles += slot_cycles; CHECK_ONLY
2207
2208#define COLUMN_RENDER_BLOCK(column, startcyc) \
2209 case startcyc:\
2210 read_map_scroll_a(column, context->vcounter, context);\
2211 CHECK_LIMIT\
2212 case ((startcyc+1)&0xFF):\
2213 external_slot(context);\
2214 CHECK_LIMIT\
2215 case ((startcyc+2)&0xFF):\
2216 render_map_1(context);\
2217 CHECK_LIMIT\
2218 case ((startcyc+3)&0xFF):\
2219 render_map_2(context);\
2220 CHECK_LIMIT\
2221 case ((startcyc+4)&0xFF):\
2222 read_map_scroll_b(column, context->vcounter, context);\
2223 CHECK_LIMIT\
2224 case ((startcyc+5)&0xFF):\
2225 read_sprite_x(context->vcounter, context);\
2226 CHECK_LIMIT\
2227 case ((startcyc+6)&0xFF):\
2228 render_map_3(context);\
2229 CHECK_LIMIT\
2230 case ((startcyc+7)&0xFF):\
2231 render_map_output(context->vcounter, column, context);\
2232 CHECK_LIMIT
2233
2234#define COLUMN_RENDER_BLOCK_REFRESH(column, startcyc) \
2235 case startcyc:\
2236 read_map_scroll_a(column, context->vcounter, context);\
2237 CHECK_LIMIT\
2238 case (startcyc+1):\
2239 /* refresh, no don't run dma src */\
2240 context->hslot++;\
2241 context->cycles += slot_cycles;\
2242 CHECK_ONLY\
2243 case (startcyc+2):\
2244 render_map_1(context);\
2245 CHECK_LIMIT\
2246 case (startcyc+3):\
2247 render_map_2(context);\
2248 CHECK_LIMIT\
2249 case (startcyc+4):\
2250 read_map_scroll_b(column, context->vcounter, context);\
2251 CHECK_LIMIT\
2252 case (startcyc+5):\
2253 read_sprite_x(context->vcounter, context);\
2254 CHECK_LIMIT\
2255 case (startcyc+6):\
2256 render_map_3(context);\
2257 CHECK_LIMIT\
2258 case (startcyc+7):\
2259 render_map_output(context->vcounter, column, context);\
2260 CHECK_LIMIT
2261
2262#define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \
2263 case startcyc:\
2264 read_map_mode4(column, context->vcounter, context);\
2265 CHECK_LIMIT\
2266 case ((startcyc+1)&0xFF):\
2267 if (column & 3) {\
2268 scan_sprite_table_mode4(context);\
2269 } else {\
2270 external_slot(context);\
2271 }\
2272 CHECK_LIMIT\
2273 case ((startcyc+2)&0xFF):\
2274 fetch_map_mode4(column, context->vcounter, context);\
2275 CHECK_LIMIT\
2276 case ((startcyc+3)&0xFF):\
2277 render_map_mode4(context->vcounter, column, context);\
2278 CHECK_LIMIT
2279
2280#define CHECK_LIMIT_HSYNC(slot) \
2281 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
2282 if (slot >= HSYNC_SLOT_H40 && slot < HSYNC_END_H40) {\
2283 context->cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];\
2284 } else {\
2285 context->cycles += slot_cycles;\
2286 }\
2287 if (slot == 182) {\
2288 context->hslot = 229;\
2289 } else {\
2290 context->hslot++;\
2291 }\
2292 CHECK_ONLY
2293
2294#define SPRITE_RENDER_H40(slot) \
2295 case slot:\
2296 if ((slot) == BG_START_SLOT + LINEBUF_SIZE/2) {\
2297 advance_output_line(context);\
2298 }\
2299 if (slot == 168 || slot == 247 || slot == 248) {\
2300 render_border_garbage(\
2301 context,\
2302 context->sprite_draw_list[context->cur_slot].address,\
2303 context->tmp_buf_b,\
2304 context->buf_b_off + (slot == 247 ? 0 : 8),\
2305 slot == 247 ? context->col_1 : context->col_2\
2306 );\
2307 if (slot == 248) {\
2308 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2309 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2310 }\
2311 } else if (slot == 243) {\
2312 render_border_garbage(\
2313 context,\
2314 context->sprite_draw_list[context->cur_slot].address,\
2315 context->tmp_buf_a,\
2316 context->buf_a_off,\
2317 context->col_1\
2318 );\
2319 } else if (slot == 169) {\
2320 draw_right_border(context);\
2321 }\
2322 render_sprite_cells( context);\
2323 scan_sprite_table(context->vcounter, context);\
2324 CHECK_LIMIT_HSYNC(slot)
2325
2326//Note that the line advancement check will fail if BG_START_SLOT is > 6
2327//as we're bumping up against the hcounter jump
2328#define SPRITE_RENDER_H32(slot) \
2329 case slot:\
2330 if ((slot) == BG_START_SLOT + (256+HORIZ_BORDER)/2) {\
2331 advance_output_line(context);\
2332 }\
2333 if (slot == 136 || slot == 247 || slot == 248) {\
2334 render_border_garbage(\
2335 context,\
2336 context->sprite_draw_list[context->cur_slot].address,\
2337 context->tmp_buf_b,\
2338 context->buf_b_off + (slot == 247 ? 0 : 8),\
2339 slot == 247 ? context->col_1 : context->col_2\
2340 );\
2341 if (slot == 248) {\
2342 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2343 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
2344 }\
2345 } else if (slot == 137) {\
2346 draw_right_border(context);\
2347 }\
2348 render_sprite_cells( context);\
2349 scan_sprite_table(context->vcounter, context);\
2350 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
2351 if (slot == 147) {\
2352 context->hslot = 233;\
2353 } else {\
2354 context->hslot++;\
2355 }\
2356 context->cycles += slot_cycles;\
2357 CHECK_ONLY
2358
2359#define MODE4_CHECK_SLOT_LINE(slot) \
2360 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
2361 if ((slot) == BG_START_SLOT + (256+HORIZ_BORDER)/2) {\
2362 advance_output_line(context);\
2363 }\
2364 if ((slot) == 147) {\
2365 context->hslot = 233;\
2366 } else {\
2367 context->hslot++;\
2368 }\
2369 context->cycles += slot_cycles;\
2370 if ((slot+1) == LINE_CHANGE_MODE4) {\
2371 vdp_advance_line(context);\
2372 if (context->vcounter == 192) {\
2373 return;\
2374 }\
2375 }\
2376 CHECK_ONLY
2377
2378#define CALC_SLOT(slot, increment) ((slot+increment) > 147 && (slot+increment) < 233 ? (slot+increment-148+233): (slot+increment))
2379
2380#define SPRITE_RENDER_H32_MODE4(slot) \
2381 case slot:\
2382 read_sprite_x_mode4(context);\
2383 MODE4_CHECK_SLOT_LINE(slot)\
2384 case CALC_SLOT(slot, 1):\
2385 read_sprite_x_mode4(context);\
2386 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot,1))\
2387 case CALC_SLOT(slot, 2):\
2388 fetch_sprite_cells_mode4(context);\
2389 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 2))\
2390 case CALC_SLOT(slot, 3):\
2391 if ((slot + 3) == 140) {\
2392 uint32_t *dst = context->output + BORDER_LEFT + 256 + 8;\
2393 uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];\
2394 for (int i = 0; i < BORDER_RIGHT-8; i++, dst++)\
2395 {\
2396 *dst = bgcolor;\
2397 }\
2398 context->done_output = dst;\
2399 }\
2400 render_sprite_cells_mode4(context);\
2401 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 3))\
2402 case CALC_SLOT(slot, 4):\
2403 fetch_sprite_cells_mode4(context);\
2404 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 4))\
2405 case CALC_SLOT(slot, 5):\
2406 render_sprite_cells_mode4(context);\
2407 MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 5))
2408
2409static void vdp_h40(vdp_context * context, uint32_t target_cycles)
2410{
2411 uint16_t address;
2412 uint32_t mask;
2413 uint32_t const slot_cycles = MCLKS_SLOT_H40;
2414 switch(context->hslot)
2415 {
2416 for (;;)
2417 {
2418 case 165:
2419 if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) {
2420 //TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode
2421 //See note in vdp_h32 for why this was originally moved out of read_map_scroll
2422 //Skitchin' has a similar problem, but uses H40 mode. It seems to be able to hit the extern slot at 232
2423 //pretty consistently
2424 context->vscroll_latch[0] = context->vsram[0];
2425 context->vscroll_latch[1] = context->vsram[1];
2426 }
2427 if (context->state == PREPARING) {
2428 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2429 uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2430 if (dst >= context->done_output) {
2431 *dst = bg_color;
2432 }
2433 dst++;
2434 if (dst >= context->done_output) {
2435 *dst = bg_color;
2436 }
2437 external_slot(context);
2438 } else {
2439 render_sprite_cells(context);
2440 }
2441 CHECK_LIMIT
2442 case 166:
2443 if (context->state == PREPARING) {
2444 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2445 uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2446 if (dst >= context->done_output) {
2447 *dst = bg_color;
2448 }
2449 dst++;
2450 if (dst >= context->done_output) {
2451 *dst = bg_color;
2452 }
2453 external_slot(context);
2454 } else {
2455 render_sprite_cells(context);
2456 }
2457 if (context->vcounter == context->inactive_start) {
2458 context->hslot++;
2459 context->cycles += slot_cycles;
2460 return;
2461 }
2462 CHECK_LIMIT
2463 //sprite attribute table scan starts
2464 case 167:
2465 if (context->state == PREPARING) {
2466 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2467 uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2468 for (int i = 0; i < LINEBUF_SIZE - 2 * (context->hslot - BG_START_SLOT); i++, dst++)
2469 {
2470 if (dst >= context->done_output) {
2471 *dst = bg_color;
2472 }
2473 }
2474 }
2475 context->sprite_index = 0x80;
2476 context->slot_counter = 0;
2477 render_border_garbage(
2478 context,
2479 context->sprite_draw_list[context->cur_slot].address,
2480 context->tmp_buf_b, context->buf_b_off,
2481 context->col_1
2482 );
2483 render_sprite_cells(context);
2484 scan_sprite_table(context->vcounter, context);
2485 CHECK_LIMIT
2486 SPRITE_RENDER_H40(168)
2487 SPRITE_RENDER_H40(169)
2488 SPRITE_RENDER_H40(170)
2489 SPRITE_RENDER_H40(171)
2490 SPRITE_RENDER_H40(172)
2491 SPRITE_RENDER_H40(173)
2492 SPRITE_RENDER_H40(174)
2493 SPRITE_RENDER_H40(175)
2494 SPRITE_RENDER_H40(176)
2495 SPRITE_RENDER_H40(177)//End of border?
2496 SPRITE_RENDER_H40(178)
2497 SPRITE_RENDER_H40(179)
2498 SPRITE_RENDER_H40(180)
2499 SPRITE_RENDER_H40(181)
2500 SPRITE_RENDER_H40(182)
2501 SPRITE_RENDER_H40(229)
2502 //!HSYNC asserted
2503 SPRITE_RENDER_H40(230)
2504 SPRITE_RENDER_H40(231)
2505 case 232:
2506 external_slot(context);
2507 CHECK_LIMIT_HSYNC(232)
2508 SPRITE_RENDER_H40(233)
2509 SPRITE_RENDER_H40(234)
2510 SPRITE_RENDER_H40(235)
2511 SPRITE_RENDER_H40(236)
2512 SPRITE_RENDER_H40(237)
2513 SPRITE_RENDER_H40(238)
2514 SPRITE_RENDER_H40(239)
2515 SPRITE_RENDER_H40(240)
2516 SPRITE_RENDER_H40(241)
2517 SPRITE_RENDER_H40(242)
2518 SPRITE_RENDER_H40(243) //provides "garbage" for border when plane A selected
2519 case 244:
2520 address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
2521 mask = 0;
2522 if (context->regs[REG_MODE_3] & 0x2) {
2523 mask |= 0xF8;
2524 }
2525 if (context->regs[REG_MODE_3] & 0x1) {
2526 mask |= 0x7;
2527 }
2528 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2);
2529 address += (context->vcounter & mask) * 4;
2530 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
2531 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
2532 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
2533 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); }
2534 context->hslot++;
2535 context->cycles += h40_hsync_cycles[14];
2536 CHECK_ONLY //provides "garbage" for border when plane A selected
2537 //!HSYNC high
2538 SPRITE_RENDER_H40(245)
2539 SPRITE_RENDER_H40(246)
2540 SPRITE_RENDER_H40(247) //provides "garbage" for border when plane B selected
2541 SPRITE_RENDER_H40(248) //provides "garbage" for border when plane B selected
2542 case 249:
2543 read_map_scroll_a(0, context->vcounter, context);
2544 CHECK_LIMIT
2545 SPRITE_RENDER_H40(250)
2546 case 251:
2547 render_map_1(context);
2548 scan_sprite_table(context->vcounter, context);//Just a guess
2549 CHECK_LIMIT
2550 case 252:
2551 render_map_2(context);
2552 scan_sprite_table(context->vcounter, context);//Just a guess
2553 CHECK_LIMIT
2554 case 253:
2555 read_map_scroll_b(0, context->vcounter, context);
2556 CHECK_LIMIT
2557 SPRITE_RENDER_H40(254)
2558 case 255:
2559 render_map_3(context);
2560 scan_sprite_table(context->vcounter, context);//Just a guess
2561 CHECK_LIMIT
2562 case 0:
2563 render_map_output(context->vcounter, 0, context);
2564 scan_sprite_table(context->vcounter, context);//Just a guess
2565 //seems like the sprite table scan fills a shift register
2566 //values are FIFO, but unused slots precede used slots
2567 //so we set cur_slot to slot_counter and let it wrap around to
2568 //the beginning of the list
2569 context->cur_slot = context->slot_counter;
2570 context->sprite_draws = MAX_DRAWS;
2571 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
2572 CHECK_LIMIT
2573 COLUMN_RENDER_BLOCK(2, 1)
2574 COLUMN_RENDER_BLOCK(4, 9)
2575 COLUMN_RENDER_BLOCK(6, 17)
2576 COLUMN_RENDER_BLOCK_REFRESH(8, 25)
2577 COLUMN_RENDER_BLOCK(10, 33)
2578 COLUMN_RENDER_BLOCK(12, 41)
2579 COLUMN_RENDER_BLOCK(14, 49)
2580 COLUMN_RENDER_BLOCK_REFRESH(16, 57)
2581 COLUMN_RENDER_BLOCK(18, 65)
2582 COLUMN_RENDER_BLOCK(20, 73)
2583 COLUMN_RENDER_BLOCK(22, 81)
2584 COLUMN_RENDER_BLOCK_REFRESH(24, 89)
2585 COLUMN_RENDER_BLOCK(26, 97)
2586 COLUMN_RENDER_BLOCK(28, 105)
2587 COLUMN_RENDER_BLOCK(30, 113)
2588 COLUMN_RENDER_BLOCK_REFRESH(32, 121)
2589 COLUMN_RENDER_BLOCK(34, 129)
2590 COLUMN_RENDER_BLOCK(36, 137)
2591 COLUMN_RENDER_BLOCK(38, 145)
2592 COLUMN_RENDER_BLOCK_REFRESH(40, 153)
2593 case 161:
2594 external_slot(context);
2595 CHECK_LIMIT
2596 case 162:
2597 external_slot(context);
2598 CHECK_LIMIT
2599 //sprite render to line buffer starts
2600 case 163:
2601 context->cur_slot = MAX_DRAWS-1;
2602 memset(context->linebuf, 0, LINEBUF_SIZE);
2603 render_border_garbage(
2604 context,
2605 context->sprite_draw_list[context->cur_slot].address,
2606 context->tmp_buf_a, context->buf_a_off,
2607 context->col_1
2608 );
2609 render_sprite_cells(context);
2610 CHECK_LIMIT
2611 case 164:
2612 render_border_garbage(
2613 context,
2614 context->sprite_draw_list[context->cur_slot].address,
2615 context->tmp_buf_a, context->buf_a_off + 8,
2616 context->col_2
2617 );
2618 render_sprite_cells(context);
2619 if (context->flags & FLAG_DMA_RUN) {
2620 run_dma_src(context, -1);
2621 }
2622 context->hslot++;
2623 context->cycles += slot_cycles;
2624 vdp_advance_line(context);
2625 CHECK_ONLY
2626 }
2627 default:
2628 context->hslot++;
2629 context->cycles += slot_cycles;
2630 return;
2631 }
2632}
2633
2634static void vdp_h32(vdp_context * context, uint32_t target_cycles)
2635{
2636 uint16_t address;
2637 uint32_t mask;
2638 uint32_t const slot_cycles = MCLKS_SLOT_H32;
2639 switch(context->hslot)
2640 {
2641 for (;;)
2642 {
2643 case 133:
2644 if (context->state == PREPARING) {
2645 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2646 uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2647 if (dst >= context->done_output) {
2648 *dst = bg_color;
2649 }
2650 dst++;
2651 if (dst >= context->done_output) {
2652 *dst = bg_color;
2653 }
2654 external_slot(context);
2655 } else {
2656 render_sprite_cells(context);
2657 }
2658 CHECK_LIMIT
2659 case 134:
2660 if (context->state == PREPARING) {
2661 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2662 uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2663 if (dst >= context->done_output) {
2664 *dst = bg_color;
2665 }
2666 dst++;
2667 if (dst >= context->done_output) {
2668 *dst = bg_color;
2669 }
2670 external_slot(context);
2671 } else {
2672 render_sprite_cells(context);
2673 }
2674 if (context->vcounter == context->inactive_start) {
2675 context->hslot++;
2676 context->cycles += slot_cycles;
2677 return;
2678 }
2679 CHECK_LIMIT
2680 //sprite attribute table scan starts
2681 case 135:
2682 if (context->state == PREPARING) {
2683 uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
2684 uint32_t *dst = context->output + (context->hslot - BG_START_SLOT) * 2;
2685 for (int i = 0; i < (256+HORIZ_BORDER) - 2 * (context->hslot - BG_START_SLOT); i++)
2686 {
2687 if (dst >= context->done_output) {
2688 *(dst++) = bg_color;
2689 }
2690 }
2691 }
2692 context->sprite_index = 0x80;
2693 context->slot_counter = 0;
2694 render_border_garbage(
2695 context,
2696 context->sprite_draw_list[context->cur_slot].address,
2697 context->tmp_buf_b, context->buf_b_off,
2698 context->col_1
2699 );
2700 render_sprite_cells(context);
2701 scan_sprite_table(context->vcounter, context);
2702 CHECK_LIMIT
2703 SPRITE_RENDER_H32(136)
2704 SPRITE_RENDER_H32(137)
2705 SPRITE_RENDER_H32(138)
2706 SPRITE_RENDER_H32(139)
2707 SPRITE_RENDER_H32(140)
2708 SPRITE_RENDER_H32(141)
2709 SPRITE_RENDER_H32(142)
2710 SPRITE_RENDER_H32(143)
2711 SPRITE_RENDER_H32(144)
2712 case 145:
2713 external_slot(context);
2714 CHECK_LIMIT
2715 SPRITE_RENDER_H32(146)
2716 SPRITE_RENDER_H32(147)
2717 SPRITE_RENDER_H32(233)
2718 SPRITE_RENDER_H32(234)
2719 SPRITE_RENDER_H32(235)
2720 //HSYNC start
2721 SPRITE_RENDER_H32(236)
2722 SPRITE_RENDER_H32(237)
2723 SPRITE_RENDER_H32(238)
2724 SPRITE_RENDER_H32(239)
2725 SPRITE_RENDER_H32(240)
2726 SPRITE_RENDER_H32(241)
2727 SPRITE_RENDER_H32(242)
2728 case 243:
2729 if (!(context->regs[REG_MODE_3] & BIT_VSCROLL)) {
2730 //TODO: Develop some tests on hardware to see when vscroll latch actually happens for full plane mode
2731 //Top Gear 2 has a very efficient HINT routine that can occassionally hit this slot with a VSRAM write
2732 //Since CRAM-updatnig HINT routines seem to indicate that my HINT latency is perhaps slightly too high
2733 //the most reasonable explanation is that vscroll is latched before this slot, but tests are needed
2734 //to confirm that one way or another
2735 context->vscroll_latch[0] = context->vsram[0];
2736 context->vscroll_latch[1] = context->vsram[1];
2737 }
2738 external_slot(context);
2739 //provides "garbage" for border when plane A selected
2740 render_border_garbage(
2741 context,
2742 context->sprite_draw_list[context->cur_slot].address,
2743 context->tmp_buf_a,
2744 context->buf_a_off,
2745 context->col_1
2746 );
2747 CHECK_LIMIT
2748 case 244:
2749 address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
2750 mask = 0;
2751 if (context->regs[REG_MODE_3] & 0x2) {
2752 mask |= 0xF8;
2753 }
2754 if (context->regs[REG_MODE_3] & 0x1) {
2755 mask |= 0x7;
2756 }
2757 render_border_garbage(context, address, context->tmp_buf_a, context->buf_a_off+8, context->col_2);
2758 address += (context->vcounter & mask) * 4;
2759 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
2760 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
2761 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
2762 CHECK_LIMIT //provides "garbage" for border when plane A selected
2763 SPRITE_RENDER_H32(245)
2764 SPRITE_RENDER_H32(246)
2765 SPRITE_RENDER_H32(247) //provides "garbage" for border when plane B selected
2766 SPRITE_RENDER_H32(248) //provides "garbage" for border when plane B selected
2767 //!HSYNC high
2768 case 249:
2769 read_map_scroll_a(0, context->vcounter, context);
2770 CHECK_LIMIT
2771 SPRITE_RENDER_H32(250)
2772 case 251:
2773 render_map_1(context);
2774 scan_sprite_table(context->vcounter, context);//Just a guess
2775 CHECK_LIMIT
2776 case 252:
2777 render_map_2(context);
2778 scan_sprite_table(context->vcounter, context);//Just a guess
2779 CHECK_LIMIT
2780 case 253:
2781 read_map_scroll_b(0, context->vcounter, context);
2782 CHECK_LIMIT
2783 case 254:
2784 render_sprite_cells(context);
2785 scan_sprite_table(context->vcounter, context);
2786 CHECK_LIMIT
2787 case 255:
2788 render_map_3(context);
2789 scan_sprite_table(context->vcounter, context);//Just a guess
2790 CHECK_LIMIT
2791 case 0:
2792 render_map_output(context->vcounter, 0, context);
2793 scan_sprite_table(context->vcounter, context);//Just a guess
2794 //reverse context slot counter so it counts the number of sprite slots
2795 //filled rather than the number of available slots
2796 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
2797 context->cur_slot = context->slot_counter;
2798 context->sprite_draws = MAX_DRAWS_H32;
2799 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
2800 CHECK_LIMIT
2801 COLUMN_RENDER_BLOCK(2, 1)
2802 COLUMN_RENDER_BLOCK(4, 9)
2803 COLUMN_RENDER_BLOCK(6, 17)
2804 COLUMN_RENDER_BLOCK_REFRESH(8, 25)
2805 COLUMN_RENDER_BLOCK(10, 33)
2806 COLUMN_RENDER_BLOCK(12, 41)
2807 COLUMN_RENDER_BLOCK(14, 49)
2808 COLUMN_RENDER_BLOCK_REFRESH(16, 57)
2809 COLUMN_RENDER_BLOCK(18, 65)
2810 COLUMN_RENDER_BLOCK(20, 73)
2811 COLUMN_RENDER_BLOCK(22, 81)
2812 COLUMN_RENDER_BLOCK_REFRESH(24, 89)
2813 COLUMN_RENDER_BLOCK(26, 97)
2814 COLUMN_RENDER_BLOCK(28, 105)
2815 COLUMN_RENDER_BLOCK(30, 113)
2816 COLUMN_RENDER_BLOCK_REFRESH(32, 121)
2817 case 129:
2818 external_slot(context);
2819 CHECK_LIMIT
2820 case 130: {
2821 external_slot(context);
2822 CHECK_LIMIT
2823 }
2824 //sprite render to line buffer starts
2825 case 131:
2826 context->cur_slot = MAX_DRAWS_H32-1;
2827 memset(context->linebuf, 0, LINEBUF_SIZE);
2828 render_border_garbage(
2829 context,
2830 context->sprite_draw_list[context->cur_slot].address,
2831 context->tmp_buf_a, context->buf_a_off,
2832 context->col_1
2833 );
2834 render_sprite_cells(context);
2835 CHECK_LIMIT
2836 case 132:
2837 render_border_garbage(
2838 context,
2839 context->sprite_draw_list[context->cur_slot].address,
2840 context->tmp_buf_a, context->buf_a_off + 8,
2841 context->col_2
2842 );
2843 render_sprite_cells(context);
2844 if (context->flags & FLAG_DMA_RUN) {
2845 run_dma_src(context, -1);
2846 }
2847 context->hslot++;
2848 context->cycles += slot_cycles;
2849 vdp_advance_line(context);
2850 CHECK_ONLY
2851 }
2852 default:
2853 context->hslot++;
2854 context->cycles += MCLKS_SLOT_H32;
2855 }
2856}
2857
2858static void vdp_h32_mode4(vdp_context * context, uint32_t target_cycles)
2859{
2860 uint16_t address;
2861 uint32_t mask;
2862 uint32_t const slot_cycles = MCLKS_SLOT_H32;
2863 switch(context->hslot)
2864 {
2865 for (;;)
2866 {
2867 //sprite rendering starts
2868 SPRITE_RENDER_H32_MODE4(137)
2869 SPRITE_RENDER_H32_MODE4(143)
2870 case 234:
2871 external_slot(context);
2872 CHECK_LIMIT
2873 case 235:
2874 external_slot(context);
2875 CHECK_LIMIT
2876 //!HSYNC low
2877 case 236:
2878 external_slot(context);
2879 CHECK_LIMIT
2880 case 237:
2881 external_slot(context);
2882 CHECK_LIMIT
2883 case 238:
2884 external_slot(context);
2885 CHECK_LIMIT
2886 SPRITE_RENDER_H32_MODE4(239)
2887 SPRITE_RENDER_H32_MODE4(245)
2888 case 251:
2889 external_slot(context);
2890 CHECK_LIMIT
2891 case 252:
2892 external_slot(context);
2893 if (context->regs[REG_MODE_1] & BIT_HSCRL_LOCK && context->vcounter < 16) {
2894 context->hscroll_a = 0;
2895 } else {
2896 context->hscroll_a = context->regs[REG_X_SCROLL];
2897 }
2898 CHECK_LIMIT
2899 case 253:
2900 context->sprite_index = 0;
2901 context->slot_counter = MAX_DRAWS_H32_MODE4;
2902 scan_sprite_table_mode4(context);
2903 CHECK_LIMIT
2904 case 254:
2905 scan_sprite_table_mode4(context);
2906 CHECK_LIMIT
2907 case 255:
2908 scan_sprite_table_mode4(context);
2909 CHECK_LIMIT
2910 case 0: {
2911 scan_sprite_table_mode4(context);
2912 uint32_t *dst = context->output;;
2913 uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
2914 for (int i = 0; i < BORDER_LEFT-8; i++, dst++)
2915 {
2916 *dst = bgcolor;
2917 }
2918 context->done_output = dst;
2919 CHECK_LIMIT
2920 }
2921 case 1:
2922 scan_sprite_table_mode4(context);
2923 CHECK_LIMIT
2924 case 2:
2925 scan_sprite_table_mode4(context);
2926 CHECK_LIMIT
2927 case 3:
2928 scan_sprite_table_mode4(context);
2929 CHECK_LIMIT
2930 case 4: {
2931 scan_sprite_table_mode4(context);
2932 context->buf_a_off = 8;
2933 memset(context->tmp_buf_a, 0, 8);
2934 uint32_t *dst = context->output + BORDER_LEFT - 8;
2935 uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
2936 for (int i = 0; i < 8; i++, dst++)
2937 {
2938 *dst = bgcolor;
2939 }
2940 context->done_output = dst;
2941 CHECK_LIMIT
2942 }
2943 COLUMN_RENDER_BLOCK_MODE4(0, 5)
2944 COLUMN_RENDER_BLOCK_MODE4(1, 9)
2945 COLUMN_RENDER_BLOCK_MODE4(2, 13)
2946 COLUMN_RENDER_BLOCK_MODE4(3, 17)
2947 COLUMN_RENDER_BLOCK_MODE4(4, 21)
2948 COLUMN_RENDER_BLOCK_MODE4(5, 25)
2949 COLUMN_RENDER_BLOCK_MODE4(6, 29)
2950 COLUMN_RENDER_BLOCK_MODE4(7, 33)
2951 COLUMN_RENDER_BLOCK_MODE4(8, 37)
2952 COLUMN_RENDER_BLOCK_MODE4(9, 41)
2953 COLUMN_RENDER_BLOCK_MODE4(10, 45)
2954 COLUMN_RENDER_BLOCK_MODE4(11, 49)
2955 COLUMN_RENDER_BLOCK_MODE4(12, 53)
2956 COLUMN_RENDER_BLOCK_MODE4(13, 57)
2957 COLUMN_RENDER_BLOCK_MODE4(14, 61)
2958 COLUMN_RENDER_BLOCK_MODE4(15, 65)
2959 COLUMN_RENDER_BLOCK_MODE4(16, 69)
2960 COLUMN_RENDER_BLOCK_MODE4(17, 73)
2961 COLUMN_RENDER_BLOCK_MODE4(18, 77)
2962 COLUMN_RENDER_BLOCK_MODE4(19, 81)
2963 COLUMN_RENDER_BLOCK_MODE4(20, 85)
2964 COLUMN_RENDER_BLOCK_MODE4(21, 89)
2965 COLUMN_RENDER_BLOCK_MODE4(22, 93)
2966 COLUMN_RENDER_BLOCK_MODE4(23, 97)
2967 COLUMN_RENDER_BLOCK_MODE4(24, 101)
2968 COLUMN_RENDER_BLOCK_MODE4(25, 105)
2969 COLUMN_RENDER_BLOCK_MODE4(26, 109)
2970 COLUMN_RENDER_BLOCK_MODE4(27, 113)
2971 COLUMN_RENDER_BLOCK_MODE4(28, 117)
2972 COLUMN_RENDER_BLOCK_MODE4(29, 121)
2973 COLUMN_RENDER_BLOCK_MODE4(30, 125)
2974 COLUMN_RENDER_BLOCK_MODE4(31, 129)
2975 case 133:
2976 external_slot(context);
2977 CHECK_LIMIT
2978 case 134:
2979 external_slot(context);
2980 CHECK_LIMIT
2981 case 135:
2982 external_slot(context);
2983 CHECK_LIMIT
2984 case 136: {
2985 external_slot(context);
2986 //set things up for sprite rendering in the next slot
2987 memset(context->linebuf, 0, LINEBUF_SIZE);
2988 context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1;
2989 context->sprite_draws = MAX_DRAWS_H32_MODE4;
2990 uint32_t *dst = context->output + BORDER_LEFT + 256;
2991 uint32_t bgcolor = context->colors[0x10 | (context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
2992 for (int i = 0; i < 8; i++, dst++)
2993 {
2994 *dst = bgcolor;
2995 }
2996 context->done_output = dst;
2997 CHECK_LIMIT
2998 }}
2999 default:
3000 context->hslot++;
3001 context->cycles += MCLKS_SLOT_H32;
3002 }
3003}
3004
3005static void inactive_test_output(vdp_context *context, uint8_t is_h40, uint8_t test_layer)
3006{
3007 uint8_t max_slot = is_h40 ? 169 : 136;
3008 if (context->hslot > max_slot) {
3009 return;
3010 }
3011 uint32_t *dst = context->output + (context->hslot >> 3) * SCROLL_BUFFER_DRAW;
3012 int32_t len;
3013 uint32_t src_off;
3014 if (context->hslot) {
3015 dst -= SCROLL_BUFFER_DRAW - BORDER_LEFT;
3016 src_off = 0;
3017 len = context->hslot == max_slot ? BORDER_RIGHT : SCROLL_BUFFER_DRAW;
3018 } else {
3019 src_off = SCROLL_BUFFER_DRAW - BORDER_LEFT;
3020 len = BORDER_LEFT;
3021 }
3022 uint8_t *src;
3023 if (test_layer == 2) {
3024 //plane A
3025 src_off += context->buf_a_off + context->hscroll_a;
3026 src = context->tmp_buf_a;
3027 } else if (test_layer == 3){
3028 //plane B
3029 src_off += context->buf_b_off + context->hscroll_b;
3030 src = context->tmp_buf_b;
3031 } else {
3032 //sprite layer
3033 for (; len >=0; len--, dst++, src_off++)
3034 {
3035 *dst = context->colors[0];
3036 }
3037 }
3038 for (; len >=0; len--, dst++, src_off++)
3039 {
3040 *dst = context->colors[src[src_off & SCROLL_BUFFER_MASK] & 0x3F];
3041 }
3042 context->done_output = dst;
3043 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_DRAW;
3044 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_DRAW;
3045}
3046
3047static void check_switch_inactive(vdp_context *context, uint8_t is_h40)
3048{
3049 //technically the second hcounter check should be different for H40, but this is probably close enough for now
3050 if (context->state == ACTIVE && context->vcounter == context->inactive_start && (context->hslot >= (is_h40 ? 167 : 135) || context->hslot < 133)) {
3051 context->state = INACTIVE;
3052 }
3053}
3054
3055static void vdp_inactive(vdp_context *context, uint32_t target_cycles, uint8_t is_h40, uint8_t mode_5)
3056{
3057 uint8_t buf_clear_slot, index_reset_slot, bg_end_slot, vint_slot, line_change, jump_start, jump_dest, latch_slot;
3058 uint8_t index_reset_value, max_draws, max_sprites;
3059 uint16_t vint_line, active_line;
3060 uint32_t bg_color;
3061
3062 if (mode_5) {
3063 if (is_h40) {
3064 latch_slot = 165;
3065 buf_clear_slot = 163;
3066 index_reset_slot = 167;
3067 bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2;
3068 max_draws = MAX_DRAWS-1;
3069 max_sprites = MAX_SPRITES_LINE;
3070 index_reset_value = 0x80;
3071 vint_slot = VINT_SLOT_H40;
3072 line_change = LINE_CHANGE_H40;
3073 jump_start = 182;
3074 jump_dest = 229;
3075 } else {
3076 bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2;
3077 max_draws = MAX_DRAWS_H32-1;
3078 max_sprites = MAX_SPRITES_LINE_H32;
3079 buf_clear_slot = 128;
3080 index_reset_slot = 132;
3081 index_reset_value = 0x80;
3082 vint_slot = VINT_SLOT_H32;
3083 line_change = LINE_CHANGE_H32;
3084 jump_start = 147;
3085 jump_dest = 233;
3086 latch_slot = 243;
3087 }
3088 vint_line = context->inactive_start;
3089 active_line = 0x1FF;
3090 if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
3091 latch_slot = 220;
3092 }
3093 } else {
3094 latch_slot = 220;
3095 bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2;
3096 max_draws = MAX_DRAWS_H32_MODE4;
3097 max_sprites = 8;
3098 buf_clear_slot = 136;
3099 index_reset_slot = 253;
3100 index_reset_value = 0;
3101 vint_line = context->inactive_start + 1;
3102 vint_slot = VINT_SLOT_MODE4;
3103 line_change = LINE_CHANGE_MODE4;
3104 bg_color = render_map_color(0, 0, 0);
3105 jump_start = 147;
3106 jump_dest = 233;
3107 if (context->regs[REG_MODE_1] & BIT_MODE_4) {
3108 active_line = 0x1FF;
3109 } else {
3110 //never active unless either mode 4 or mode 5 is turned on
3111 active_line = 0x200;
3112 }
3113 }
3114 uint32_t *dst;
3115 uint8_t *debug_dst;
3116 if (
3117 (
3118 context->vcounter < context->inactive_start + context->border_bot
3119 || context->vcounter >= 0x200 - context->border_top
3120 ) && context->hslot >= BG_START_SLOT && context->hslot < bg_end_slot
3121 ) {
3122 dst = context->output + 2 * (context->hslot - BG_START_SLOT);
3123 debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
3124 } else {
3125 dst = NULL;
3126 }
3127
3128 if (
3129 !dst && context->vcounter == context->inactive_start + context->border_bot
3130 && context->hslot >= line_change && context->hslot < bg_end_slot
3131 ) {
3132 dst = context->output + 2 * (context->hslot - BG_START_SLOT);
3133 debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
3134 }
3135
3136 uint8_t test_layer = context->test_port >> 7 & 3;
3137 if (test_layer) {
3138 dst = NULL;
3139 }
3140
3141 while(context->cycles < target_cycles)
3142 {
3143 check_switch_inactive(context, is_h40);
3144 if (context->hslot == BG_START_SLOT && !test_layer && (
3145 context->vcounter < context->inactive_start + context->border_bot
3146 || context->vcounter >= 0x200 - context->border_top
3147 )) {
3148 dst = context->output + (context->hslot - BG_START_SLOT) * 2;
3149 debug_dst = context->layer_debug_buf + 2 * (context->hslot - BG_START_SLOT);
3150 } else if (context->hslot == bg_end_slot) {
3151 advance_output_line(context);
3152 dst = NULL;
3153 }
3154 //this will need some tweaking to properly interact with 128K mode,
3155 //but this should be good enough for now
3156 context->serial_address += 1024;
3157 if (test_layer) {
3158 switch (context->hslot & 7)
3159 {
3160 case 3:
3161 render_border_garbage(context, context->serial_address, context->tmp_buf_a, context->buf_a_off, context->col_1);
3162 break;
3163 case 4:
3164 render_border_garbage(context, context->serial_address, context->tmp_buf_a, context->buf_a_off+8, context->col_2);
3165 break;
3166 case 7:
3167 render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off, context->col_1);
3168 break;
3169 case 0:
3170 render_border_garbage(context, context->serial_address, context->tmp_buf_b, context->buf_b_off+8, context->col_2);
3171 inactive_test_output(context, is_h40, test_layer);
3172 break;
3173 }
3174 }
3175
3176 if (context->hslot == buf_clear_slot) {
3177 if (mode_5) {
3178 context->cur_slot = max_draws;
3179 } else {
3180 context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1;
3181 context->sprite_draws = MAX_DRAWS_H32_MODE4;
3182 }
3183 memset(context->linebuf, 0, LINEBUF_SIZE);
3184 } else if (context->hslot == index_reset_slot) {
3185 context->sprite_index = index_reset_value;
3186 context->slot_counter = mode_5 ? 0 : max_sprites;
3187 } else if (context->hslot == latch_slot) {
3188 //it seems unlikely to me that vscroll actually gets latched when the display is off
3189 //but it's the only straightforward way to reconcile what I'm seeing between Skitchin
3190 //(which seems to expect vscroll to be latched early) and the intro of Gunstar Heroes
3191 //(which disables the display and ends up with garbage if vscroll is latched during that period)
3192 //without it. Some more tests are definitely needed
3193 context->vscroll_latch[0] = context->vsram[0];
3194 context->vscroll_latch[1] = context->vsram[1];
3195 } else if (context->vcounter == vint_line && context->hslot == vint_slot) {
3196 context->flags2 |= FLAG2_VINT_PENDING;
3197 context->pending_vint_start = context->cycles;
3198 } else if (context->vcounter == context->inactive_start && context->hslot == 1 && (context->regs[REG_MODE_4] & BIT_INTERLACE)) {
3199 context->flags2 ^= FLAG2_EVEN_FIELD;
3200 }
3201
3202 if (dst) {
3203 if (mode_5) {
3204 bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
3205 } else if (context->regs[REG_MODE_1] & BIT_MODE_4) {
3206 bg_color = context->colors[MODE4_OFFSET + 0x10 + (context->regs[REG_BG_COLOR] & 0xF)];
3207 }
3208 if (dst >= context->done_output) {
3209 *(dst++) = bg_color;
3210 *(debug_dst++) = DBG_SRC_BG;
3211 } else {
3212 dst++;
3213 debug_dst++;
3214 }
3215 if (dst >= context->done_output) {
3216 *(dst++) = bg_color;
3217 *(debug_dst++) = DBG_SRC_BG;
3218 context->done_output = dst;
3219 } else {
3220 dst++;
3221 debug_dst++;
3222 }
3223 if (context->hslot == (bg_end_slot-1)) {
3224 *(dst++) = bg_color;
3225 *(debug_dst++) = DBG_SRC_BG;
3226 context->done_output = dst;
3227 }
3228 }
3229
3230 if (!is_refresh(context, context->hslot)) {
3231 external_slot(context);
3232 if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) {
3233 run_dma_src(context, context->hslot);
3234 }
3235 }
3236
3237 if (is_h40) {
3238 if (context->hslot >= HSYNC_SLOT_H40 && context->hslot < HSYNC_END_H40) {
3239 context->cycles += h40_hsync_cycles[context->hslot - HSYNC_SLOT_H40];
3240 } else {
3241 context->cycles += MCLKS_SLOT_H40;
3242 }
3243 } else {
3244 context->cycles += MCLKS_SLOT_H32;
3245 }
3246 if (context->hslot == jump_start) {
3247 context->hslot = jump_dest;
3248 } else {
3249 context->hslot++;
3250 }
3251 if (context->hslot == line_change) {
3252 vdp_advance_line(context);
3253 if (context->vcounter == active_line) {
3254 context->state = PREPARING;
3255 return;
3256 }
3257 }
3258 }
3259}
3260
3261void vdp_run_context_full(vdp_context * context, uint32_t target_cycles)
3262{
3263 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40;
3264 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
3265 while(context->cycles < target_cycles)
3266 {
3267 check_switch_inactive(context, is_h40);
3268
3269 if (is_active(context)) {
3270 if (mode_5) {
3271 if (is_h40) {
3272 vdp_h40(context, target_cycles);
3273 } else {
3274 vdp_h32(context, target_cycles);
3275 }
3276 } else {
3277 vdp_h32_mode4(context, target_cycles);
3278 }
3279 } else {
3280 vdp_inactive(context, target_cycles, is_h40, mode_5);
3281 }
3282 }
3283}
3284
3285void vdp_run_context(vdp_context *context, uint32_t target_cycles)
3286{
3287 //TODO: Deal with H40 hsync shenanigans
3288 uint32_t slot_cyc = context->regs[REG_MODE_4] & BIT_H40 ? 15 : 19;
3289 if (target_cycles < slot_cyc) {
3290 //avoid overflow
3291 return;
3292 }
3293 vdp_run_context_full(context, target_cycles - slot_cyc);
3294}
3295
3296uint32_t vdp_run_to_vblank(vdp_context * context)
3297{
3298 uint32_t old_frame = context->frame;
3299 while (context->frame == old_frame) {
3300 vdp_run_context_full(context, context->cycles + MCLKS_LINE);
3301 }
3302 return context->cycles;
3303}
3304
3305void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles)
3306{
3307 for(;;) {
3308 uint32_t dmalen = (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L];
3309 if (!dmalen) {
3310 dmalen = 0x10000;
3311 }
3312 uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20);
3313 if (
3314 (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_COPY
3315 || (((context->cd & 0xF) == VRAM_WRITE) && !(context->regs[REG_MODE_2] & BIT_128K_VRAM))) {
3316 //DMA copies take twice as long to complete since they require a read and a write
3317 //DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word
3318 //unless 128KB mode is enabled
3319 min_dma_complete *= 2;
3320 }
3321 min_dma_complete += context->cycles;
3322 if (target_cycles < min_dma_complete) {
3323 vdp_run_context_full(context, target_cycles);
3324 return;
3325 } else {
3326 vdp_run_context_full(context, min_dma_complete);
3327 if (!(context->flags & FLAG_DMA_RUN)) {
3328 return;
3329 }
3330 }
3331 }
3332}
3333
3334static uint16_t get_ext_vcounter(vdp_context *context)
3335{
3336 uint16_t line= context->vcounter;
3337 if (context->regs[REG_MODE_4] & BIT_INTERLACE) {
3338 if (context->double_res) {
3339 line <<= 1;
3340 } else {
3341 line &= 0x1FE;
3342 }
3343 if (line & 0x100) {
3344 line |= 1;
3345 }
3346 }
3347 return line << 8;
3348}
3349
3350void vdp_latch_hv(vdp_context *context)
3351{
3352 context->hv_latch = context->hslot | get_ext_vcounter(context);
3353}
3354
3355uint16_t vdp_hv_counter_read(vdp_context * context)
3356{
3357 if ((context->regs[REG_MODE_2] & BIT_MODE_5) && (context->regs[REG_MODE_1] & BIT_HVC_LATCH)) {
3358 return context->hv_latch;
3359 }
3360 uint16_t hv;
3361 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3362 hv = context->hslot;
3363 } else {
3364 hv = context->hv_latch & 0xFF;
3365 }
3366 hv |= get_ext_vcounter(context);
3367
3368 return hv;
3369}
3370
3371int vdp_control_port_write(vdp_context * context, uint16_t value)
3372{
3373 //printf("control port write: %X at %d\n", value, context->cycles);
3374 if (context->flags & FLAG_DMA_RUN) {
3375 return -1;
3376 }
3377 if (context->flags & FLAG_PENDING) {
3378 context->address = (context->address & 0x3FFF) | (value << 14 & 0x1C000);
3379 //It seems like the DMA enable bit doesn't so much enable DMA so much
3380 //as it enables changing CD5 from control port writes
3381 uint8_t preserve = (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) ? 0x3 : 0x23;
3382 context->cd = (context->cd & preserve) | ((value >> 2) & ~preserve & 0xFF);
3383 context->flags &= ~FLAG_PENDING;
3384 //Should these be taken care of here or after the first write?
3385 context->flags &= ~FLAG_READ_FETCHED;
3386 context->flags2 &= ~FLAG2_READ_PENDING;
3387 //printf("New Address: %X, New CD: %X\n", context->address, context->cd);
3388 if (context->cd & 0x20) {
3389 //
3390 if((context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) {
3391 //DMA copy or 68K -> VDP, transfer starts immediately
3392 //printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot);
3393 if (!(context->regs[REG_DMASRC_H] & 0x80)) {
3394 //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]);
3395 //68K -> VDP DMA takes a few slots to actually start reading even though it acquires the bus immediately
3396 //logic analyzer captures made it seem like the proper value is 4 slots, but that seems to cause trouble with the Nemesis' FIFO Wait State test
3397 //only captures are from a direct color DMA demo which will generally start DMA at a very specific point in display so other values are plausible
3398 //sticking with 3 slots for now until I can do some more captures
3399 vdp_run_context_full(context, context->cycles + 12 * ((context->regs[REG_MODE_2] & BIT_MODE_5) && (context->regs[REG_MODE_4] & BIT_H40) ? 4 : 5));
3400 context->flags |= FLAG_DMA_RUN;
3401 return 1;
3402 } else {
3403 context->flags |= FLAG_DMA_RUN;
3404 //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
3405 }
3406 } else {
3407 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd);
3408 }
3409 }
3410 } else {
3411 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
3412 context->address = (context->address &0xC000) | (value & 0x3FFF);
3413 context->cd = (context->cd & 0x3C) | (value >> 14);
3414 if ((value & 0xC000) == 0x8000) {
3415 //Register write
3416 uint8_t reg = (value >> 8) & 0x1F;
3417 if (reg < (mode_5 ? VDP_REGS : 0xB)) {
3418 //printf("register %d set to %X\n", reg, value & 0xFF);
3419 if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
3420 vdp_latch_hv(context);
3421 }
3422 if (reg == REG_BG_COLOR) {
3423 value &= 0x3F;
3424 }
3425 /*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) {
3426 printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame);
3427 }*/
3428 context->regs[reg] = value;
3429 if (reg == REG_MODE_4) {
3430 context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
3431 if (!context->double_res) {
3432 context->flags2 &= ~FLAG2_EVEN_FIELD;
3433 }
3434 }
3435 if (reg == REG_MODE_1 || reg == REG_MODE_2 || reg == REG_MODE_4) {
3436 update_video_params(context);
3437 }
3438 }
3439 } else if (mode_5) {
3440 context->flags |= FLAG_PENDING;
3441 //Should these be taken care of here or after the second write?
3442 //context->flags &= ~FLAG_READ_FETCHED;
3443 //context->flags2 &= ~FLAG2_READ_PENDING;
3444 } else {
3445 context->flags &= ~FLAG_READ_FETCHED;
3446 context->flags2 &= ~FLAG2_READ_PENDING;
3447 }
3448 }
3449 return 0;
3450}
3451
3452void vdp_control_port_write_pbc(vdp_context *context, uint8_t value)
3453{
3454 if (context->flags2 & FLAG2_BYTE_PENDING) {
3455 uint16_t full_val = value << 8 | context->pending_byte;
3456 context->flags2 &= ~FLAG2_BYTE_PENDING;
3457 //TODO: Deal with fact that Vbus->VDP DMA doesn't do anything in PBC mode
3458 vdp_control_port_write(context, full_val);
3459 if (context->cd == VRAM_READ) {
3460 context->cd = VRAM_READ8;
3461 }
3462 } else {
3463 context->pending_byte = value;
3464 context->flags2 |= FLAG2_BYTE_PENDING;
3465 }
3466}
3467
3468int vdp_data_port_write(vdp_context * context, uint16_t value)
3469{
3470 //printf("data port write: %X at %d\n", value, context->cycles);
3471 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) != DMA_FILL) {
3472 return -1;
3473 }
3474 if (context->flags & FLAG_PENDING) {
3475 context->flags &= ~FLAG_PENDING;
3476 //Should these be cleared here?
3477 context->flags &= ~FLAG_READ_FETCHED;
3478 context->flags2 &= ~FLAG2_READ_PENDING;
3479 }
3480 /*if (context->fifo_cur == context->fifo_end) {
3481 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
3482 }*/
3483 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
3484 context->flags &= ~FLAG_DMA_RUN;
3485 }
3486 while (context->fifo_write == context->fifo_read) {
3487 vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
3488 }
3489 fifo_entry * cur = context->fifo + context->fifo_write;
3490 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
3491 cur->address = context->address;
3492 cur->value = value;
3493 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3494 cur->cd = context->cd;
3495 } else {
3496 cur->cd = (context->cd & 2) | 1;
3497 }
3498 cur->partial = 0;
3499 if (context->fifo_read < 0) {
3500 context->fifo_read = context->fifo_write;
3501 }
3502 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
3503 increment_address(context);
3504 return 0;
3505}
3506
3507void vdp_data_port_write_pbc(vdp_context * context, uint8_t value)
3508{
3509 if (context->flags & FLAG_PENDING) {
3510 context->flags &= ~FLAG_PENDING;
3511 //Should these be cleared here?
3512 context->flags &= ~FLAG_READ_FETCHED;
3513 context->flags2 &= ~FLAG2_READ_PENDING;
3514 }
3515 context->flags2 &= ~FLAG2_BYTE_PENDING;
3516 /*if (context->fifo_cur == context->fifo_end) {
3517 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
3518 }*/
3519 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) {
3520 context->flags &= ~FLAG_DMA_RUN;
3521 }
3522 while (context->fifo_write == context->fifo_read) {
3523 vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
3524 }
3525 fifo_entry * cur = context->fifo + context->fifo_write;
3526 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
3527 cur->address = context->address;
3528 cur->value = value;
3529 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3530 cur->cd = context->cd;
3531 } else {
3532 cur->cd = (context->cd & 2) | 1;
3533 }
3534 cur->partial = 3;
3535 if (context->fifo_read < 0) {
3536 context->fifo_read = context->fifo_write;
3537 }
3538 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
3539 increment_address(context);
3540}
3541
3542void vdp_test_port_write(vdp_context * context, uint16_t value)
3543{
3544 context->test_port = value;
3545}
3546
3547uint16_t vdp_control_port_read(vdp_context * context)
3548{
3549 context->flags &= ~FLAG_PENDING;
3550 context->flags2 &= ~FLAG2_BYTE_PENDING;
3551 //Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch
3552 uint16_t value = context->system->get_open_bus_value(context->system) & 0xFC00;
3553 if (context->fifo_read < 0) {
3554 value |= 0x200;
3555 }
3556 if (context->fifo_read == context->fifo_write) {
3557 value |= 0x100;
3558 }
3559 if (context->flags2 & FLAG2_VINT_PENDING) {
3560 value |= 0x80;
3561 }
3562 if (context->flags & FLAG_DOT_OFLOW) {
3563 value |= 0x40;
3564 context->flags &= ~FLAG_DOT_OFLOW;
3565 }
3566 if (context->flags2 & FLAG2_SPRITE_COLLIDE) {
3567 value |= 0x20;
3568 context->flags2 &= ~FLAG2_SPRITE_COLLIDE;
3569 }
3570 if ((context->regs[REG_MODE_4] & BIT_INTERLACE) && !(context->flags2 & FLAG2_EVEN_FIELD)) {
3571 value |= 0x10;
3572 }
3573 uint32_t slot = context->hslot;
3574 if (!is_active(context)) {
3575 value |= 0x8;
3576 }
3577 if (context->regs[REG_MODE_4] & BIT_H40) {
3578 if (slot < HBLANK_END_H40 || slot > HBLANK_START_H40) {
3579 value |= 0x4;
3580 }
3581 } else {
3582 if (slot < HBLANK_END_H32 || slot > HBLANK_START_H32) {
3583 value |= 0x4;
3584 }
3585 }
3586 if (context->cd & 0x20) {
3587 value |= 0x2;
3588 }
3589 if (context->flags2 & FLAG2_REGION_PAL) {
3590 value |= 0x1;
3591 }
3592 //printf("status read at cycle %d returned %X\n", context->cycles, value);
3593 return value;
3594}
3595
3596uint16_t vdp_data_port_read(vdp_context * context)
3597{
3598 if (context->flags & FLAG_PENDING) {
3599 context->flags &= ~FLAG_PENDING;
3600 //Should these be cleared here?
3601 context->flags &= ~FLAG_READ_FETCHED;
3602 context->flags2 &= ~FLAG2_READ_PENDING;
3603 }
3604 if (context->cd & 1) {
3605 warning("Read from VDP data port while writes are configured, CPU is now frozen. VDP Address: %X, CD: %X\n", context->address, context->cd);
3606 }
3607 while (!(context->flags & FLAG_READ_FETCHED)) {
3608 vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
3609 }
3610 context->flags &= ~FLAG_READ_FETCHED;
3611 return context->prefetch;
3612}
3613
3614uint8_t vdp_data_port_read_pbc(vdp_context * context)
3615{
3616 context->flags &= ~(FLAG_PENDING | FLAG_READ_FETCHED);
3617 context->flags2 &= ~FLAG2_BYTE_PENDING;
3618
3619 context->cd = VRAM_READ8;
3620 return context->prefetch;
3621}
3622
3623uint16_t vdp_test_port_read(vdp_context * context)
3624{
3625 //TODO: Find out what actually gets returned here
3626 return context->test_port;
3627}
3628
3629void vdp_adjust_cycles(vdp_context * context, uint32_t deduction)
3630{
3631 context->cycles -= deduction;
3632 if (context->pending_vint_start >= deduction) {
3633 context->pending_vint_start -= deduction;
3634 } else {
3635 context->pending_vint_start = 0;
3636 }
3637 if (context->pending_hint_start >= deduction) {
3638 context->pending_hint_start -= deduction;
3639 } else {
3640 context->pending_hint_start = 0;
3641 }
3642 if (context->fifo_read >= 0) {
3643 int32_t idx = context->fifo_read;
3644 do {
3645 if (context->fifo[idx].cycle >= deduction) {
3646 context->fifo[idx].cycle -= deduction;
3647 } else {
3648 context->fifo[idx].cycle = 0;
3649 }
3650 idx = (idx+1) & (FIFO_SIZE-1);
3651 } while(idx != context->fifo_write);
3652 }
3653}
3654
3655static uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context)
3656{
3657 if (context->hslot < 183) {
3658 return MCLKS_LINE - context->hslot * MCLKS_SLOT_H40;
3659 } else if (context->hslot < HSYNC_END_H40) {
3660 uint32_t before_hsync = context->hslot < HSYNC_SLOT_H40 ? (HSYNC_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40 : 0;
3661 uint32_t hsync = 0;
3662 for (int i = context->hslot <= HSYNC_SLOT_H40 ? 0 : context->hslot - HSYNC_SLOT_H40; i < sizeof(h40_hsync_cycles)/sizeof(uint32_t); i++)
3663 {
3664 hsync += h40_hsync_cycles[i];
3665 }
3666 uint32_t after_hsync = (256- HSYNC_END_H40) * MCLKS_SLOT_H40;
3667 return before_hsync + hsync + after_hsync;
3668 } else {
3669 return (256-context->hslot) * MCLKS_SLOT_H40;
3670 }
3671}
3672
3673static uint32_t vdp_cycles_next_line(vdp_context * context)
3674{
3675 if (context->regs[REG_MODE_4] & BIT_H40) {
3676 //TODO: Handle "illegal" Mode 4/H40 combo
3677 if (context->hslot < LINE_CHANGE_H40) {
3678 return (LINE_CHANGE_H40 - context->hslot) * MCLKS_SLOT_H40;
3679 } else {
3680 return vdp_cycles_hslot_wrap_h40(context) + LINE_CHANGE_H40 * MCLKS_SLOT_H40;
3681 }
3682 } else {
3683 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3684 if (context->hslot < LINE_CHANGE_H32) {
3685 return (LINE_CHANGE_H32 - context->hslot) * MCLKS_SLOT_H32;
3686 } else if (context->hslot < 148) {
3687 return MCLKS_LINE - (context->hslot - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
3688 } else {
3689 return (256-context->hslot + LINE_CHANGE_H32) * MCLKS_SLOT_H32;
3690 }
3691 } else {
3692 if (context->hslot < 148) {
3693 return (148 - context->hslot + LINE_CHANGE_MODE4 - 233) * MCLKS_SLOT_H32;
3694 } else if (context->hslot < LINE_CHANGE_MODE4) {
3695 return (LINE_CHANGE_MODE4 - context->hslot) * MCLKS_SLOT_H32;
3696 } else {
3697 return MCLKS_LINE - (context->hslot - LINE_CHANGE_MODE4) * MCLKS_SLOT_H32;
3698 }
3699 }
3700 }
3701}
3702
3703static void get_jump_params(vdp_context *context, uint32_t *jump_start, uint32_t *jump_dst)
3704{
3705 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3706 if (context->flags2 & FLAG2_REGION_PAL) {
3707 if (context->regs[REG_MODE_2] & BIT_PAL) {
3708 *jump_start = 0x10B;
3709 *jump_dst = 0x1D2;
3710 } else {
3711 *jump_start = 0x103;
3712 *jump_dst = 0x1CA;
3713 }
3714 } else {
3715 if (context->regs[REG_MODE_2] & BIT_PAL) {
3716 *jump_start = 0x100;
3717 *jump_dst = 0x1FA;
3718 } else {
3719 *jump_start = 0xEB;
3720 *jump_dst = 0x1E5;
3721 }
3722 }
3723 } else {
3724 *jump_start = 0xDB;
3725 *jump_dst = 0x1D5;
3726 }
3727}
3728
3729static uint32_t vdp_cycles_to_line(vdp_context * context, uint32_t target)
3730{
3731 uint32_t jump_start, jump_dst;
3732 get_jump_params(context, &jump_start, &jump_dst);
3733 uint32_t lines;
3734 if (context->vcounter < target) {
3735 if (target < jump_start || context->vcounter > jump_start) {
3736 lines = target - context->vcounter;
3737 } else {
3738 lines = jump_start - context->vcounter + target - jump_dst;
3739 }
3740 } else {
3741 if (context->vcounter < jump_start) {
3742 lines = jump_start - context->vcounter + 512 - jump_dst;
3743 } else {
3744 lines = 512 - context->vcounter;
3745 }
3746 if (target < jump_start) {
3747 lines += target;
3748 } else {
3749 lines += jump_start + target - jump_dst;
3750 }
3751 }
3752 return MCLKS_LINE * (lines - 1) + vdp_cycles_next_line(context);
3753}
3754
3755uint32_t vdp_cycles_to_frame_end(vdp_context * context)
3756{
3757 return context->cycles + vdp_cycles_to_line(context, context->inactive_start);
3758}
3759
3760uint32_t vdp_next_hint(vdp_context * context)
3761{
3762 if (!(context->regs[REG_MODE_1] & BIT_HINT_EN)) {
3763 return 0xFFFFFFFF;
3764 }
3765 if (context->flags2 & FLAG2_HINT_PENDING) {
3766 return context->pending_hint_start;
3767 }
3768 uint32_t hint_line;
3769 if (context->state != ACTIVE) {
3770 hint_line = context->regs[REG_HINT];
3771 if (hint_line > context->inactive_start) {
3772 return 0xFFFFFFFF;
3773 }
3774 } else {
3775 hint_line = context->vcounter + context->hint_counter + 1;
3776 if (context->vcounter < context->inactive_start) {
3777 if (hint_line > context->inactive_start) {
3778 hint_line = context->regs[REG_HINT];
3779 if (hint_line > context->inactive_start) {
3780 return 0xFFFFFFFF;
3781 }
3782 if (hint_line >= context->vcounter) {
3783 //Next interrupt is for a line in the next frame that
3784 //is higher than the line we're on now so just passing
3785 //that line number to vdp_cycles_to_line will yield the wrong
3786 //result
3787 return context->cycles + vdp_cycles_to_line(context, 0) + hint_line * MCLKS_LINE;
3788 }
3789 }
3790 } else {
3791 uint32_t jump_start, jump_dst;
3792 get_jump_params(context, &jump_start, &jump_dst);
3793 if (hint_line >= jump_start && context->vcounter < jump_dst) {
3794 hint_line = (hint_line + jump_dst - jump_start) & 0x1FF;
3795 }
3796 if (hint_line < context->vcounter && hint_line > context->inactive_start) {
3797 return 0xFFFFFFFF;
3798 }
3799 }
3800 }
3801 return context->cycles + vdp_cycles_to_line(context, hint_line);
3802}
3803
3804static uint32_t vdp_next_vint_real(vdp_context * context)
3805{
3806 if (!(context->regs[REG_MODE_2] & BIT_VINT_EN)) {
3807 return 0xFFFFFFFF;
3808 }
3809 if (context->flags2 & FLAG2_VINT_PENDING) {
3810 return context->pending_vint_start;
3811 }
3812
3813
3814 return vdp_next_vint_z80(context);
3815}
3816
3817uint32_t vdp_next_vint(vdp_context *context)
3818{
3819 uint32_t ret = vdp_next_vint_real(context);
3820#ifdef TIMING_DEBUG
3821 static uint32_t last = 0xFFFFFFFF;
3822 if (last != ret) {
3823 printf("vdp_next_vint is %d at frame %d, line %d, hslot %d\n", ret, context->frame, context->vcounter, context->hslot);
3824 }
3825 last = ret;
3826#endif
3827 return ret;
3828}
3829
3830uint32_t vdp_next_vint_z80(vdp_context * context)
3831{
3832 uint16_t vint_line = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->inactive_start : context->inactive_start + 1;
3833 if (context->vcounter == vint_line) {
3834 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3835 if (context->regs[REG_MODE_4] & BIT_H40) {
3836 if (context->hslot >= LINE_CHANGE_H40 || context->hslot <= VINT_SLOT_H40) {
3837 uint32_t cycles = context->cycles;
3838 if (context->hslot >= LINE_CHANGE_H40) {
3839 if (context->hslot < 183) {
3840 cycles += (183 - context->hslot) * MCLKS_SLOT_H40;
3841 }
3842
3843 if (context->hslot < HSYNC_SLOT_H40) {
3844 cycles += (HSYNC_SLOT_H40 - (context->hslot >= 229 ? context->hslot : 229)) * MCLKS_SLOT_H40;
3845 }
3846 for (int slot = context->hslot <= HSYNC_SLOT_H40 ? HSYNC_SLOT_H40 : context->hslot; slot < HSYNC_END_H40; slot++ )
3847 {
3848 cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];
3849 }
3850 cycles += (256 - (context->hslot > HSYNC_END_H40 ? context->hslot : HSYNC_END_H40)) * MCLKS_SLOT_H40;
3851 }
3852
3853 cycles += (VINT_SLOT_H40 - (context->hslot >= LINE_CHANGE_H40 ? 0 : context->hslot)) * MCLKS_SLOT_H40;
3854 return cycles;
3855 }
3856 } else {
3857 if (context->hslot >= LINE_CHANGE_H32 || context->hslot <= VINT_SLOT_H32) {
3858 if (context->hslot <= VINT_SLOT_H32) {
3859 return context->cycles + (VINT_SLOT_H32 - context->hslot) * MCLKS_SLOT_H32;
3860 } else if (context->hslot < 233) {
3861 return context->cycles + (VINT_SLOT_H32 + 256 - 233 + 148 - context->hslot) * MCLKS_SLOT_H32;
3862 } else {
3863 return context->cycles + (VINT_SLOT_H32 + 256 - context->hslot) * MCLKS_SLOT_H32;
3864 }
3865 }
3866 }
3867 } else {
3868 if (context->hslot >= LINE_CHANGE_MODE4) {
3869 return context->cycles + (VINT_SLOT_MODE4 + 256 - context->hslot) * MCLKS_SLOT_H32;
3870 }
3871 if (context->hslot <= VINT_SLOT_MODE4) {
3872 return context->cycles + (VINT_SLOT_MODE4 - context->hslot) * MCLKS_SLOT_H32;
3873 }
3874 }
3875 }
3876 int32_t cycles_to_vint = vdp_cycles_to_line(context, vint_line);
3877 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3878 if (context->regs[REG_MODE_4] & BIT_H40) {
3879 cycles_to_vint += MCLKS_LINE - (LINE_CHANGE_H40 - VINT_SLOT_H40) * MCLKS_SLOT_H40;
3880 } else {
3881 cycles_to_vint += (VINT_SLOT_H32 + 256 - 233 + 148 - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
3882 }
3883 } else {
3884 cycles_to_vint += (256 - LINE_CHANGE_MODE4 + VINT_SLOT_MODE4) * MCLKS_SLOT_H32;
3885 }
3886 return context->cycles + cycles_to_vint;
3887}
3888
3889uint32_t vdp_next_nmi(vdp_context *context)
3890{
3891 if (!(context->flags2 & FLAG2_PAUSE)) {
3892 return 0xFFFFFFFF;
3893 }
3894 return context->cycles + vdp_cycles_to_line(context, 0x1FF);
3895}
3896
3897void vdp_pbc_pause(vdp_context *context)
3898{
3899 context->flags2 |= FLAG2_PAUSE;
3900}
3901
3902void vdp_int_ack(vdp_context * context)
3903{
3904 //CPU interrupt acknowledge is only used in Mode 5
3905 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
3906 //Apparently the VDP interrupt controller is not very smart
3907 //Instead of paying attention to what interrupt is being acknowledged it just
3908 //clears the pending flag for whatever interrupt it is currently asserted
3909 //which may be different from the interrupt it was asserting when the 68k
3910 //started the interrupt process. The window for this is narrow and depends
3911 //on the latency between the int enable register write and the interrupt being
3912 //asserted, but Fatal Rewind depends on this due to some buggy code
3913 if ((context->flags2 & FLAG2_VINT_PENDING) && (context->regs[REG_MODE_2] & BIT_VINT_EN)) {
3914 context->flags2 &= ~FLAG2_VINT_PENDING;
3915 } else if((context->flags2 & FLAG2_HINT_PENDING) && (context->regs[REG_MODE_1] & BIT_HINT_EN)) {
3916 context->flags2 &= ~FLAG2_HINT_PENDING;
3917 }
3918 }
3919}
3920
3921void vdp_serialize(vdp_context *context, serialize_buffer *buf)
3922{
3923 save_int8(buf, VRAM_SIZE / 1024);//VRAM size in KB, needed for future proofing
3924 save_buffer8(buf, context->vdpmem, VRAM_SIZE);
3925 save_buffer16(buf, context->cram, CRAM_SIZE);
3926 save_buffer16(buf, context->vsram, VSRAM_SIZE);
3927 save_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE);
3928 for (int i = 0; i <= REG_DMASRC_H; i++)
3929 {
3930 save_int8(buf, context->regs[i]);
3931 }
3932 save_int32(buf, context->address);
3933 save_int32(buf, context->serial_address);
3934 save_int8(buf, context->cd);
3935 uint8_t fifo_size;
3936 if (context->fifo_read < 0) {
3937 fifo_size = 0;
3938 } else if (context->fifo_write > context->fifo_read) {
3939 fifo_size = context->fifo_write - context->fifo_read;
3940 } else {
3941 fifo_size = context->fifo_write + FIFO_SIZE - context->fifo_read;
3942 }
3943 save_int8(buf, fifo_size);
3944 for (int i = 0, cur = context->fifo_read; i < fifo_size; i++)
3945 {
3946 fifo_entry *entry = context->fifo + cur;
3947 cur = (cur + 1) & (FIFO_SIZE - 1);
3948 save_int32(buf, entry->cycle);
3949 save_int32(buf, entry->address);
3950 save_int16(buf, entry->value);
3951 save_int8(buf, entry->cd);
3952 save_int8(buf, entry->partial);
3953 }
3954 //FIXME: Flag bits should be rearranged for maximum correspondence to status reg
3955 save_int16(buf, context->flags2 << 8 | context->flags);
3956 save_int32(buf, context->frame);
3957 save_int16(buf, context->vcounter);
3958 save_int8(buf, context->hslot);
3959 save_int16(buf, context->hv_latch);
3960 save_int8(buf, context->state);
3961 save_int16(buf, context->hscroll_a);
3962 save_int16(buf, context->hscroll_b);
3963 save_int16(buf, context->vscroll_latch[0]);
3964 save_int16(buf, context->vscroll_latch[1]);
3965 save_int16(buf, context->col_1);
3966 save_int16(buf, context->col_2);
3967 save_int16(buf, context->test_port);
3968 save_buffer8(buf, context->tmp_buf_a, SCROLL_BUFFER_SIZE);
3969 save_buffer8(buf, context->tmp_buf_b, SCROLL_BUFFER_SIZE);
3970 save_int8(buf, context->buf_a_off);
3971 save_int8(buf, context->buf_b_off);
3972 //FIXME: Sprite rendering state is currently a mess
3973 save_int8(buf, context->sprite_index);
3974 save_int8(buf, context->sprite_draws);
3975 save_int8(buf, context->slot_counter);
3976 save_int8(buf, context->cur_slot);
3977 for (int i = 0; i < MAX_DRAWS; i++)
3978 {
3979 sprite_draw *draw = context->sprite_draw_list + i;
3980 save_int16(buf, draw->address);
3981 save_int16(buf, draw->x_pos);
3982 save_int8(buf, draw->pal_priority);
3983 save_int8(buf, draw->h_flip);
3984 }
3985 for (int i = 0; i < MAX_SPRITES_LINE; i++)
3986 {
3987 sprite_info *info = context->sprite_info_list + i;
3988 save_int8(buf, info->size);
3989 save_int8(buf, info->index);
3990 save_int16(buf, info->y);
3991 }
3992 save_buffer8(buf, context->linebuf, LINEBUF_SIZE);
3993
3994 save_int32(buf, context->cycles);
3995 save_int32(buf, context->pending_vint_start);
3996 save_int32(buf, context->pending_hint_start);
3997}
3998
3999void vdp_deserialize(deserialize_buffer *buf, void *vcontext)
4000{
4001 vdp_context *context = vcontext;
4002 uint8_t vramk = load_int8(buf);
4003 load_buffer8(buf, context->vdpmem, (vramk * 1024) <= VRAM_SIZE ? vramk * 1024 : VRAM_SIZE);
4004 if ((vramk * 1024) > VRAM_SIZE) {
4005 buf->cur_pos += (vramk * 1024) - VRAM_SIZE;
4006 }
4007 load_buffer16(buf, context->cram, CRAM_SIZE);
4008 for (int i = 0; i < CRAM_SIZE; i++)
4009 {
4010 update_color_map(context, i, context->cram[i]);
4011 }
4012 load_buffer16(buf, context->vsram, VSRAM_SIZE);
4013 load_buffer8(buf, context->sat_cache, SAT_CACHE_SIZE);
4014 for (int i = 0; i <= REG_DMASRC_H; i++)
4015 {
4016 context->regs[i] = load_int8(buf);
4017 }
4018 context->address = load_int32(buf);
4019 context->serial_address = load_int32(buf);
4020 context->cd = load_int8(buf);
4021 uint8_t fifo_size = load_int8(buf);
4022 if (fifo_size > FIFO_SIZE) {
4023 fatal_error("Invalid fifo size %d", fifo_size);
4024 }
4025 if (fifo_size) {
4026 context->fifo_read = 0;
4027 context->fifo_write = fifo_size & (FIFO_SIZE - 1);
4028 for (int i = 0; i < fifo_size; i++)
4029 {
4030 fifo_entry *entry = context->fifo + i;
4031 entry->cycle = load_int32(buf);
4032 entry->address = load_int32(buf);
4033 entry->value = load_int16(buf);
4034 entry->cd = load_int8(buf);
4035 entry->partial = load_int8(buf);
4036 }
4037 } else {
4038 context->fifo_read = -1;
4039 context->fifo_write = 0;
4040 }
4041 uint16_t flags = load_int16(buf);
4042 context->flags2 = flags >> 8;
4043 context->flags = flags;
4044 context->frame = load_int32(buf);
4045 context->vcounter = load_int16(buf);
4046 context->hslot = load_int8(buf);
4047 context->hv_latch = load_int16(buf);
4048 context->state = load_int8(buf);
4049 context->hscroll_a = load_int16(buf);
4050 context->hscroll_b = load_int16(buf);
4051 context->vscroll_latch[0] = load_int16(buf);
4052 context->vscroll_latch[1] = load_int16(buf);
4053 context->col_1 = load_int16(buf);
4054 context->col_2 = load_int16(buf);
4055 context->test_port = load_int16(buf);
4056 load_buffer8(buf, context->tmp_buf_a, SCROLL_BUFFER_SIZE);
4057 load_buffer8(buf, context->tmp_buf_b, SCROLL_BUFFER_SIZE);
4058 context->buf_a_off = load_int8(buf) & SCROLL_BUFFER_MASK;
4059 context->buf_b_off = load_int8(buf) & SCROLL_BUFFER_MASK;
4060 context->sprite_index = load_int8(buf);
4061 context->sprite_draws = load_int8(buf);
4062 context->slot_counter = load_int8(buf);
4063 context->cur_slot = load_int8(buf);
4064 for (int i = 0; i < MAX_DRAWS; i++)
4065 {
4066 sprite_draw *draw = context->sprite_draw_list + i;
4067 draw->address = load_int16(buf);
4068 draw->x_pos = load_int16(buf);
4069 draw->pal_priority = load_int8(buf);
4070 draw->h_flip = load_int8(buf);
4071 }
4072 for (int i = 0; i < MAX_SPRITES_LINE; i++)
4073 {
4074 sprite_info *info = context->sprite_info_list + i;
4075 info->size = load_int8(buf);
4076 info->index = load_int8(buf);
4077 info->y = load_int16(buf);
4078 }
4079 load_buffer8(buf, context->linebuf, LINEBUF_SIZE);
4080
4081 context->cycles = load_int32(buf);
4082 context->pending_vint_start = load_int32(buf);
4083 context->pending_hint_start = load_int32(buf);
4084 update_video_params(context);
4085}
4086
4087static vdp_context *current_vdp;
4088static void vdp_debug_window_close(uint8_t which)
4089{
4090 //TODO: remove need for current_vdp global, and find the VDP via current_system instead
4091 for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++)
4092 {
4093 if (current_vdp->enabled_debuggers & (1 << i) && which == current_vdp->debug_fb_indices[i]) {
4094 vdp_toggle_debug_view(current_vdp, i);
4095 break;
4096 }
4097 }
4098}
4099
4100void vdp_toggle_debug_view(vdp_context *context, uint8_t debug_type)
4101{
4102 if (context->enabled_debuggers & 1 << debug_type) {
4103 render_destroy_window(context->debug_fb_indices[debug_type]);
4104 context->enabled_debuggers &= ~(1 << debug_type);
4105 } else {
4106 uint32_t width,height;
4107 uint8_t fetch_immediately = 0;
4108 char *caption;
4109 switch(debug_type)
4110 {
4111 case VDP_DEBUG_PLANE:
4112 caption = "BlastEm - VDP Plane Debugger";
4113 width = height = 1024;
4114 break;
4115 case VDP_DEBUG_VRAM:
4116 caption = "BlastEm - VDP VRAM Debugger";
4117 width = 1024;
4118 height = 512;
4119 break;
4120 case VDP_DEBUG_CRAM:
4121 caption = "BlastEm - VDP CRAM Debugger";
4122 width = 512;
4123 height = 512;
4124 fetch_immediately = 1;
4125 break;
4126 case VDP_DEBUG_COMPOSITE:
4127 caption = "BlastEm - VDP Plane Composition Debugger";
4128 width = LINEBUF_SIZE;
4129 height = context->inactive_start + context->border_top + context->border_bot;
4130 fetch_immediately = 1;
4131 break;
4132 default:
4133 return;
4134 }
4135 current_vdp = context;
4136 context->debug_fb_indices[debug_type] = render_create_window(caption, width, height, vdp_debug_window_close);
4137 if (context->debug_fb_indices[debug_type]) {
4138 context->enabled_debuggers |= 1 << debug_type;
4139 }
4140 if (fetch_immediately) {
4141 context->debug_fbs[debug_type] = render_get_video_buffer(context->debug_fb_indices[debug_type], &context->debug_fb_pitch[debug_type]);
4142 }
4143 }
4144}
4145
4146void vdp_inc_debug_mode(vdp_context *context)
4147{
4148 uint8_t active = render_get_active_video_buffer();
4149 if (active < VIDEO_BUFFER_USER_START) {
4150 return;
4151 }
4152 for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++)
4153 {
4154 if (context->enabled_debuggers & (1 << i) && context->debug_fb_indices[i] == active) {
4155 context->debug_modes[i]++;
4156 return;
4157 }
4158 }
4159}