comparison vdp.c @ 2686:05915f01046d

WIP attempt to move VDP rendering to a separate thread
author Michael Pavone <pavone@retrodev.com>
date Mon, 31 Mar 2025 21:02:17 -0700
parents da2e06c42d16
children
comparison
equal deleted inserted replaced
2685:da2e06c42d16 2686:05915f01046d
154 } 154 }
155 } 155 }
156 156
157 static uint8_t static_table_init_done; 157 static uint8_t static_table_init_done;
158 158
159 vdp_context *init_vdp_context(uint8_t region_pal, uint8_t has_max_vsram, uint8_t type) 159 vdp_context *init_vdp_context_int(uint8_t region_pal, uint8_t has_max_vsram, uint8_t type)
160 { 160 {
161 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE); 161 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE);
162 if (headless) {
163 context->fb = malloc(512 * LINEBUF_SIZE * sizeof(pixel_t));
164 context->output_pitch = LINEBUF_SIZE * sizeof(pixel_t);
165 } else {
166 context->cur_buffer = FRAMEBUFFER_ODD;
167 context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
168 }
169 context->sprite_draws = MAX_SPRITES_LINE; 162 context->sprite_draws = MAX_SPRITES_LINE;
170 context->fifo_write = 0; 163 context->fifo_write = 0;
171 context->fifo_read = -1; 164 context->fifo_read = -1;
172 context->regs[REG_HINT] = context->hint_counter = 0xFF; 165 context->regs[REG_HINT] = context->hint_counter = 0xFF;
173 context->vsram_size = has_max_vsram ? MAX_VSRAM_SIZE : MIN_VSRAM_SIZE; 166 context->vsram_size = has_max_vsram ? MAX_VSRAM_SIZE : MIN_VSRAM_SIZE;
337 } 330 }
338 if (region_pal) { 331 if (region_pal) {
339 context->flags2 |= FLAG2_REGION_PAL; 332 context->flags2 |= FLAG2_REGION_PAL;
340 } 333 }
341 update_video_params(context); 334 update_video_params(context);
335
336 return context;
337 }
338
339 static uint32_t mode5_sat_address(vdp_context *context)
340 {
341 uint32_t addr = context->regs[REG_SAT] << 9;
342 if (!(context->regs[REG_MODE_2] & BIT_128K_VRAM)) {
343 addr &= 0xFFFF;
344 }
345 if (context->regs[REG_MODE_4] & BIT_H40) {
346 addr &= 0x1FC00;
347 }
348 return addr;
349 }
350
351 static void vdp_check_update_sat(vdp_context *context, uint32_t address, uint16_t value)
352 {
353 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
354 if (!(address & 4)) {
355 uint32_t sat_address = mode5_sat_address(context);
356 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
357 uint16_t cache_address = address - sat_address;
358 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
359 context->sat_cache[cache_address] = value >> 8;
360 context->sat_cache[cache_address^1] = value;
361 }
362 }
363 }
364 }
365
366 void vdp_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t value)
367 {
368 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
369 if (!(address & 4)) {
370 uint32_t sat_address = mode5_sat_address(context);
371 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
372 uint16_t cache_address = address - sat_address;
373 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
374 context->sat_cache[cache_address] = value;
375 }
376 }
377 }
378 }
379
380 static void write_vram_word(vdp_context *context, uint32_t address, uint16_t value)
381 {
382 address = (address & 0x3FC) | (address >> 1 & 0xFC01) | (address >> 9 & 0x2);
383 address ^= 1;
384 //TODO: Support an option to actually have 128KB of VRAM
385 context->vdpmem[address] = value;
386 }
387
388 static void write_vram_byte(vdp_context *context, uint32_t address, uint8_t value)
389 {
390 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
391 address &= 0xFFFF;
392 } else {
393 address = mode4_address_map[address & 0x3FFF];
394 }
395 context->vdpmem[address] = value;
396 }
397
398 #define VSRAM_DIRTY_BITS 0xF800
399
400 //rough estimate of slot number at which border display starts
401 #define BG_START_SLOT 6
402
403 static void update_color_map(vdp_context *context, uint16_t index, uint16_t value)
404 {
405 context->colors[index] = context->color_map[value & CRAM_BITS];
406 context->colors[index + SHADOW_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_SHADOW];
407 context->colors[index + HIGHLIGHT_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_HILIGHT];
408 if (context->type == VDP_GAMEGEAR) {
409 context->colors[index + MODE4_OFFSET] = context->color_map[value & 0xFFF];
410 } else {
411 context->colors[index + MODE4_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_MODE4];
412 }
413 }
414
415 void write_cram_internal(vdp_context * context, uint16_t addr, uint16_t value)
416 {
417 context->cram[addr] = value;
418 update_color_map(context, addr, value);
419 }
420
421 static void write_cram(vdp_context * context, uint16_t address, uint16_t value)
422 {
423 uint16_t addr;
424 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
425 addr = (address/2) & (CRAM_SIZE-1);
426 } else if (context->type == VDP_GAMEGEAR) {
427 addr = (address/2) & 31;
428 } else {
429 addr = address & 0x1F;
430 value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00);
431 }
432 write_cram_internal(context, addr, value);
433
434 if (context->output && context->hslot >= BG_START_SLOT && (
435 context->vcounter < context->inactive_start + context->border_bot
436 || context->vcounter > 0x200 - context->border_top
437 )) {
438 uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2;
439 if (context->hslot < bg_end_slot) {
440 pixel_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET];
441 context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color;
442 }
443 }
444 }
445
446 #ifndef _WIN32
447 static int vdp_render_thread_main(void *vcontext)
448 {
449 vdp_context *context = vcontext;
450 event_out event;
451 for (;;)
452 {
453 event.autoinc = context->regs[REG_AUTOINC];
454 uint8_t etype = mem_reader_next_event(&event);
455 if (etype == EVENT_EOF) {
456 break;
457 }
458 vdp_run_context(context, event.cycle);
459 switch (etype)
460 {
461 case EVENT_ADJUST:
462 vdp_adjust_cycles(context, event.address);
463 break;
464 case EVENT_VDP_REG:
465 context->regs[event.address] = event.value;
466 if (event.address == REG_MODE_4) {
467 context->double_res = (event.value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
468 if (!context->double_res) {
469 context->flags2 &= ~FLAG2_EVEN_FIELD;
470 }
471 }
472 if (event.address == REG_MODE_1 || event.address == REG_MODE_2 || event.address == REG_MODE_4) {
473 update_video_params(context);
474 }
475 break;
476 case EVENT_VRAM_BYTE:
477 case EVENT_VRAM_BYTE_DELTA:
478 case EVENT_VRAM_BYTE_ONE:
479 case EVENT_VRAM_BYTE_AUTO:
480 vdp_check_update_sat_byte(context, event.address ^ 1, event.value);
481 write_vram_byte(context, event.address ^ 1, event.value);
482 break;
483 case EVENT_VRAM_WORD:
484 case EVENT_VRAM_WORD_DELTA:
485 vdp_check_update_sat(context, event.address, event.value);
486 write_vram_word(context, event.address, event.value);
487 break;
488 case EVENT_VDP_INTRAM:
489 if (event.address < 128) {
490 write_cram(context, event.address, event.value);
491 } else {
492 context->vsram[event.address&63] = event.value;
493 }
494 break;
495 }
496 }
497 return 0;
498 }
499 #endif
500
501 static render_thread vdp_thread;
502 vdp_context *init_vdp_context(uint8_t region_pal, uint8_t has_max_vsram, uint8_t type)
503 {
504 vdp_context *ret = init_vdp_context_int(region_pal, has_max_vsram, type);
505 vdp_context *context;
506 #ifndef _WIN32
507 if (render_is_threaded_video()) {
508 context = ret->renderer = init_vdp_context_int(region_pal, has_max_vsram, type);
509 } else
510 #endif
511 {
512 context = ret;
513 }
514 if (headless) {
515 context->fb = malloc(512 * LINEBUF_SIZE * sizeof(pixel_t));
516 context->output_pitch = LINEBUF_SIZE * sizeof(pixel_t);
517 } else {
518 context->cur_buffer = FRAMEBUFFER_ODD;
519 context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
520 }
342 context->output = (pixel_t *)(((char *)context->fb) + context->output_pitch * context->border_top); 521 context->output = (pixel_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
343 return context; 522 #ifndef _WIN32
523 if (ret->renderer) {
524 event_log_mem();
525 render_create_thread(&vdp_thread, "vdp_render", vdp_render_thread_main, ret->renderer);
526 }
527 #endif
528 return ret;
344 } 529 }
345 530
346 void vdp_free(vdp_context *context) 531 void vdp_free(vdp_context *context)
347 { 532 {
348 if (headless) { 533 if (headless) {
352 { 537 {
353 if (context->enabled_debuggers & (1 << i)) { 538 if (context->enabled_debuggers & (1 << i)) {
354 vdp_toggle_debug_view(context, i); 539 vdp_toggle_debug_view(context, i);
355 } 540 }
356 } 541 }
542 #ifndef _WIN32
543 if (context->renderer) {
544 event_log_mem_stop();
545 vdp_free(context->renderer);
546 }
547 #endif
357 free(context); 548 free(context);
358 } 549 }
359 550
360 static int is_refresh(vdp_context * context, uint32_t slot) 551 static int is_refresh(vdp_context * context, uint32_t slot)
361 { 552 {
516 } 707 }
517 } 708 }
518 } 709 }
519 context->sprite_index--; 710 context->sprite_index--;
520 } 711 }
521 }
522
523 static uint32_t mode5_sat_address(vdp_context *context)
524 {
525 uint32_t addr = context->regs[REG_SAT] << 9;
526 if (!(context->regs[REG_MODE_2] & BIT_128K_VRAM)) {
527 addr &= 0xFFFF;
528 }
529 if (context->regs[REG_MODE_4] & BIT_H40) {
530 addr &= 0x1FC00;
531 }
532 return addr;
533 } 712 }
534 713
535 void vdp_print_sprite_table(vdp_context * context) 714 void vdp_print_sprite_table(vdp_context * context)
536 { 715 {
537 if (context->type == VDP_GENESIS && context->regs[REG_MODE_2] & BIT_MODE_5) { 716 if (context->type == VDP_GENESIS && context->regs[REG_MODE_2] & BIT_MODE_5) {
955 context->sprite_draw_list[context->sprite_draws].address = tile_address; 1134 context->sprite_draw_list[context->sprite_draws].address = tile_address;
956 context->cur_slot--; 1135 context->cur_slot--;
957 } 1136 }
958 } 1137 }
959 1138
960 #define VSRAM_DIRTY_BITS 0xF800
961
962 //rough estimate of slot number at which border display starts
963 #define BG_START_SLOT 6
964
965 static void update_color_map(vdp_context *context, uint16_t index, uint16_t value)
966 {
967 context->colors[index] = context->color_map[value & CRAM_BITS];
968 context->colors[index + SHADOW_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_SHADOW];
969 context->colors[index + HIGHLIGHT_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_HILIGHT];
970 if (context->type == VDP_GAMEGEAR) {
971 context->colors[index + MODE4_OFFSET] = context->color_map[value & 0xFFF];
972 } else {
973 context->colors[index + MODE4_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_MODE4];
974 }
975 }
976
977 void write_cram_internal(vdp_context * context, uint16_t addr, uint16_t value)
978 {
979 context->cram[addr] = value;
980 update_color_map(context, addr, value);
981 }
982
983 static void write_cram(vdp_context * context, uint16_t address, uint16_t value)
984 {
985 uint16_t addr;
986 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
987 addr = (address/2) & (CRAM_SIZE-1);
988 } else if (context->type == VDP_GAMEGEAR) {
989 addr = (address/2) & 31;
990 } else {
991 addr = address & 0x1F;
992 value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00);
993 }
994 write_cram_internal(context, addr, value);
995
996 if (context->output && context->hslot >= BG_START_SLOT && (
997 context->vcounter < context->inactive_start + context->border_bot
998 || context->vcounter > 0x200 - context->border_top
999 )) {
1000 uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2;
1001 if (context->hslot < bg_end_slot) {
1002 pixel_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET];
1003 context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color;
1004 }
1005 }
1006 }
1007
1008 static void vdp_advance_dma(vdp_context * context) 1139 static void vdp_advance_dma(vdp_context * context)
1009 { 1140 {
1010 context->regs[REG_DMASRC_L] += 1; 1141 context->regs[REG_DMASRC_L] += 1;
1011 if (!context->regs[REG_DMASRC_L]) { 1142 if (!context->regs[REG_DMASRC_L]) {
1012 context->regs[REG_DMASRC_M] += 1; 1143 context->regs[REG_DMASRC_M] += 1;
1017 context->regs[REG_DMALEN_L] = dma_len; 1148 context->regs[REG_DMALEN_L] = dma_len;
1018 if (!dma_len) { 1149 if (!dma_len) {
1019 context->flags &= ~FLAG_DMA_RUN; 1150 context->flags &= ~FLAG_DMA_RUN;
1020 context->cd &= 0xF; 1151 context->cd &= 0xF;
1021 } 1152 }
1022 }
1023
1024 static void vdp_check_update_sat(vdp_context *context, uint32_t address, uint16_t value)
1025 {
1026 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
1027 if (!(address & 4)) {
1028 uint32_t sat_address = mode5_sat_address(context);
1029 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
1030 uint16_t cache_address = address - sat_address;
1031 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
1032 context->sat_cache[cache_address] = value >> 8;
1033 context->sat_cache[cache_address^1] = value;
1034 }
1035 }
1036 }
1037 }
1038
1039 void vdp_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t value)
1040 {
1041 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
1042 if (!(address & 4)) {
1043 uint32_t sat_address = mode5_sat_address(context);
1044 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) {
1045 uint16_t cache_address = address - sat_address;
1046 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC);
1047 context->sat_cache[cache_address] = value;
1048 }
1049 }
1050 }
1051 }
1052
1053 static void write_vram_word(vdp_context *context, uint32_t address, uint16_t value)
1054 {
1055 address = (address & 0x3FC) | (address >> 1 & 0xFC01) | (address >> 9 & 0x2);
1056 address ^= 1;
1057 //TODO: Support an option to actually have 128KB of VRAM
1058 context->vdpmem[address] = value;
1059 }
1060
1061 static void write_vram_byte(vdp_context *context, uint32_t address, uint8_t value)
1062 {
1063 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
1064 address &= 0xFFFF;
1065 } else {
1066 address = mode4_address_map[address & 0x3FFF];
1067 }
1068 context->vdpmem[address] = value;
1069 } 1153 }
1070 1154
1071 #define DMA_FILL 0x80 1155 #define DMA_FILL 0x80
1072 #define DMA_COPY 0xC0 1156 #define DMA_COPY 0xC0
1073 #define DMA_TYPE_MASK 0xC0 1157 #define DMA_TYPE_MASK 0xC0
2146 } 2230 }
2147 } 2231 }
2148 } 2232 }
2149 2233
2150 context->vcounter++; 2234 context->vcounter++;
2235 if (context->renderer && context->vcounter == context->inactive_start) {
2236 context->frame++;
2237 }
2151 if (is_mode_5) { 2238 if (is_mode_5) {
2152 context->window_h_latch = context->regs[REG_WINDOW_H]; 2239 context->window_h_latch = context->regs[REG_WINDOW_H];
2153 context->window_v_latch = context->regs[REG_WINDOW_V]; 2240 context->window_v_latch = context->regs[REG_WINDOW_V];
2154 } 2241 }
2155 if (context->vcounter == jump_start) { 2242 if (context->vcounter == jump_start) {
3109 case (startcyc+7):\ 3196 case (startcyc+7):\
3110 OUTPUT_PIXEL((startcyc+7)&0xFF)\ 3197 OUTPUT_PIXEL((startcyc+7)&0xFF)\
3111 render_map_output(context->vcounter, column, context);\ 3198 render_map_output(context->vcounter, column, context);\
3112 CHECK_LIMIT 3199 CHECK_LIMIT
3113 3200
3201 #define COLUMN_RENDER_BLOCK_PHONY(column, startcyc) \
3202 case startcyc:\
3203 CHECK_LIMIT\
3204 case ((startcyc+1)&0xFF):\
3205 external_slot(context);\
3206 CHECK_LIMIT\
3207 case ((startcyc+2)&0xFF):\
3208 CHECK_LIMIT\
3209 case ((startcyc+3)&0xFF):\
3210 CHECK_LIMIT\
3211 case ((startcyc+4)&0xFF):\
3212 CHECK_LIMIT\
3213 case ((startcyc+5)&0xFF):\
3214 read_sprite_x(context->vcounter, context);\
3215 CHECK_LIMIT\
3216 case ((startcyc+6)&0xFF):\
3217 CHECK_LIMIT\
3218 case ((startcyc+7)&0xFF):\
3219 CHECK_LIMIT
3220
3221 #define COLUMN_RENDER_BLOCK_REFRESH_PHONY(column, startcyc) \
3222 case startcyc:\
3223 CHECK_LIMIT\
3224 case (startcyc+1):\
3225 /* refresh, so don't run dma src */\
3226 context->hslot++;\
3227 context->cycles += slot_cycles;\
3228 CHECK_ONLY\
3229 case (startcyc+2):\
3230 CHECK_LIMIT\
3231 case (startcyc+3):\
3232 CHECK_LIMIT\
3233 case (startcyc+4):\
3234 CHECK_LIMIT\
3235 case (startcyc+5):\
3236 read_sprite_x(context->vcounter, context);\
3237 CHECK_LIMIT\
3238 case (startcyc+6):\
3239 CHECK_LIMIT\
3240 case (startcyc+7):\
3241 CHECK_LIMIT
3242
3114 #define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \ 3243 #define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \
3115 case startcyc:\ 3244 case startcyc:\
3116 OUTPUT_PIXEL_MODE4(startcyc)\ 3245 OUTPUT_PIXEL_MODE4(startcyc)\
3117 read_map_mode4(column, context->vcounter, context);\ 3246 read_map_mode4(column, context->vcounter, context);\
3118 CHECK_LIMIT\ 3247 CHECK_LIMIT\
3181 draw_right_border(context);\ 3310 draw_right_border(context);\
3182 }\ 3311 }\
3183 scan_sprite_table(context->vcounter, context);\ 3312 scan_sprite_table(context->vcounter, context);\
3184 CHECK_LIMIT_HSYNC(slot) 3313 CHECK_LIMIT_HSYNC(slot)
3185 3314
3315 #define SPRITE_RENDER_H40_PHONY(slot) \
3316 case slot:\
3317 render_sprite_cells( context);\
3318 scan_sprite_table(context->vcounter, context);\
3319 CHECK_LIMIT_HSYNC(slot)
3320
3186 //Note that the line advancement check will fail if BG_START_SLOT is > 6 3321 //Note that the line advancement check will fail if BG_START_SLOT is > 6
3187 //as we're bumping up against the hcounter jump 3322 //as we're bumping up against the hcounter jump
3188 #define SPRITE_RENDER_H32(slot) \ 3323 #define SPRITE_RENDER_H32(slot) \
3189 case slot:\ 3324 case slot:\
3190 OUTPUT_PIXEL_H32(slot)\ 3325 OUTPUT_PIXEL_H32(slot)\
3208 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\ 3343 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;\
3209 }\ 3344 }\
3210 } else if (slot == 137) {\ 3345 } else if (slot == 137) {\
3211 draw_right_border(context);\ 3346 draw_right_border(context);\
3212 }\ 3347 }\
3348 scan_sprite_table(context->vcounter, context);\
3349 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
3350 if (slot == 147) {\
3351 context->hslot = 233;\
3352 } else {\
3353 context->hslot++;\
3354 }\
3355 context->cycles += slot_cycles;\
3356 CHECK_ONLY
3357
3358 #define SPRITE_RENDER_H32_PHONY(slot) \
3359 case slot:\
3360 render_sprite_cells( context);\
3213 scan_sprite_table(context->vcounter, context);\ 3361 scan_sprite_table(context->vcounter, context);\
3214 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ 3362 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
3215 if (slot == 147) {\ 3363 if (slot == 147) {\
3216 context->hslot = 233;\ 3364 context->hslot = 233;\
3217 } else {\ 3365 } else {\
3705 context->cycles += slot_cycles; 3853 context->cycles += slot_cycles;
3706 return; 3854 return;
3707 } 3855 }
3708 } 3856 }
3709 3857
3858 static void vdp_h40_phony(vdp_context * context, uint32_t target_cycles)
3859 {
3860 uint32_t const slot_cycles = MCLKS_SLOT_H40;
3861 switch(context->hslot)
3862 {
3863 for (;;)
3864 {
3865 case 165:
3866 if (context->state == PREPARING) {
3867 external_slot(context);
3868 } else {
3869 render_sprite_cells(context);
3870 }
3871 CHECK_LIMIT
3872 case 166:
3873 if (context->state == PREPARING) {
3874 external_slot(context);
3875 } else {
3876 render_sprite_cells(context);
3877 }
3878 if (context->vcounter == context->inactive_start) {
3879 context->hslot++;
3880 context->cycles += slot_cycles;
3881 return;
3882 }
3883 CHECK_LIMIT
3884 //sprite attribute table scan starts
3885 case 167:
3886 context->sprite_index = 0x80;
3887 context->slot_counter = 0;
3888 render_sprite_cells(context);
3889 scan_sprite_table(context->vcounter, context);
3890 CHECK_LIMIT
3891 SPRITE_RENDER_H40_PHONY(168)
3892 SPRITE_RENDER_H40_PHONY(169)
3893 SPRITE_RENDER_H40_PHONY(170)
3894 SPRITE_RENDER_H40_PHONY(171)
3895 SPRITE_RENDER_H40_PHONY(172)
3896 SPRITE_RENDER_H40_PHONY(173)
3897 SPRITE_RENDER_H40_PHONY(174)
3898 SPRITE_RENDER_H40_PHONY(175)
3899 SPRITE_RENDER_H40_PHONY(176)
3900 SPRITE_RENDER_H40_PHONY(177)//End of border?
3901 SPRITE_RENDER_H40_PHONY(178)
3902 SPRITE_RENDER_H40_PHONY(179)
3903 SPRITE_RENDER_H40_PHONY(180)
3904 SPRITE_RENDER_H40_PHONY(181)
3905 SPRITE_RENDER_H40_PHONY(182)
3906 SPRITE_RENDER_H40_PHONY(229)
3907 //!HSYNC asserted
3908 SPRITE_RENDER_H40_PHONY(230)
3909 SPRITE_RENDER_H40_PHONY(231)
3910 case 232:
3911 external_slot(context);
3912 CHECK_LIMIT_HSYNC(232)
3913 SPRITE_RENDER_H40_PHONY(233)
3914 SPRITE_RENDER_H40_PHONY(234)
3915 SPRITE_RENDER_H40_PHONY(235)
3916 SPRITE_RENDER_H40_PHONY(236)
3917 SPRITE_RENDER_H40_PHONY(237)
3918 SPRITE_RENDER_H40_PHONY(238)
3919 SPRITE_RENDER_H40_PHONY(239)
3920 SPRITE_RENDER_H40_PHONY(240)
3921 SPRITE_RENDER_H40_PHONY(241)
3922 SPRITE_RENDER_H40_PHONY(242)
3923 SPRITE_RENDER_H40_PHONY(243) //provides "garbage" for border when plane A selected
3924 case 244:
3925 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); }
3926 context->hslot++;
3927 context->cycles += h40_hsync_cycles[14];
3928 CHECK_ONLY //provides "garbage" for border when plane A selected
3929 //!HSYNC high
3930 SPRITE_RENDER_H40_PHONY(245)
3931 SPRITE_RENDER_H40_PHONY(246)
3932 SPRITE_RENDER_H40_PHONY(247) //provides "garbage" for border when plane B selected
3933 SPRITE_RENDER_H40_PHONY(248) //provides "garbage" for border when plane B selected
3934 case 249:
3935 CHECK_LIMIT
3936 SPRITE_RENDER_H40_PHONY(250)
3937 case 251:
3938 scan_sprite_table(context->vcounter, context);//Just a guess
3939 CHECK_LIMIT
3940 case 252:
3941 scan_sprite_table(context->vcounter, context);//Just a guess
3942 CHECK_LIMIT
3943 case 253:
3944 CHECK_LIMIT
3945 SPRITE_RENDER_H40_PHONY(254)
3946 case 255:
3947 scan_sprite_table(context->vcounter, context);//Just a guess
3948 CHECK_LIMIT
3949 case 0:
3950 scan_sprite_table(context->vcounter, context);//Just a guess
3951 //seems like the sprite table scan fills a shift register
3952 //values are FIFO, but unused slots precede used slots
3953 //so we set cur_slot to slot_counter and let it wrap around to
3954 //the beginning of the list
3955 context->cur_slot = context->slot_counter;
3956 context->sprite_x_offset = 0;
3957 context->sprite_draws = MAX_SPRITES_LINE;
3958 CHECK_LIMIT
3959 COLUMN_RENDER_BLOCK_PHONY(2, 1)
3960 COLUMN_RENDER_BLOCK_PHONY(4, 9)
3961 COLUMN_RENDER_BLOCK_PHONY(6, 17)
3962 COLUMN_RENDER_BLOCK_REFRESH_PHONY(8, 25)
3963 COLUMN_RENDER_BLOCK_PHONY(10, 33)
3964 COLUMN_RENDER_BLOCK_PHONY(12, 41)
3965 COLUMN_RENDER_BLOCK_PHONY(14, 49)
3966 COLUMN_RENDER_BLOCK_REFRESH_PHONY(16, 57)
3967 COLUMN_RENDER_BLOCK_PHONY(18, 65)
3968 COLUMN_RENDER_BLOCK_PHONY(20, 73)
3969 COLUMN_RENDER_BLOCK_PHONY(22, 81)
3970 COLUMN_RENDER_BLOCK_REFRESH_PHONY(24, 89)
3971 COLUMN_RENDER_BLOCK_PHONY(26, 97)
3972 COLUMN_RENDER_BLOCK_PHONY(28, 105)
3973 COLUMN_RENDER_BLOCK_PHONY(30, 113)
3974 COLUMN_RENDER_BLOCK_REFRESH_PHONY(32, 121)
3975 COLUMN_RENDER_BLOCK_PHONY(34, 129)
3976 COLUMN_RENDER_BLOCK_PHONY(36, 137)
3977 COLUMN_RENDER_BLOCK_PHONY(38, 145)
3978 COLUMN_RENDER_BLOCK_REFRESH_PHONY(40, 153)
3979 case 161:
3980 external_slot(context);
3981 CHECK_LIMIT
3982 case 162:
3983 external_slot(context);
3984 CHECK_LIMIT
3985 //sprite render to line buffer starts
3986 case 163:
3987 context->cur_slot = MAX_SPRITES_LINE-1;
3988 memset(context->linebuf, 0, LINEBUF_SIZE);
3989 context->flags &= ~FLAG_MASKED;
3990 while (context->sprite_draws) {
3991 context->sprite_draws--;
3992 context->sprite_draw_list[context->sprite_draws].x_pos = 0;
3993 }
3994 render_sprite_cells(context);
3995 CHECK_LIMIT
3996 case 164:
3997 render_sprite_cells(context);
3998 if (context->flags & FLAG_DMA_RUN) {
3999 run_dma_src(context, -1);
4000 }
4001 context->hslot++;
4002 context->cycles += slot_cycles;
4003 vdp_advance_line(context);
4004 CHECK_ONLY
4005 }
4006 default:
4007 context->hslot++;
4008 context->cycles += slot_cycles;
4009 return;
4010 }
4011 }
4012
3710 static void vdp_h32(vdp_context * context, uint32_t target_cycles) 4013 static void vdp_h32(vdp_context * context, uint32_t target_cycles)
3711 { 4014 {
3712 uint16_t address; 4015 uint16_t address;
3713 uint32_t mask; 4016 uint32_t mask;
3714 uint32_t const slot_cycles = MCLKS_SLOT_H32; 4017 uint32_t const slot_cycles = MCLKS_SLOT_H32;
3909 context, 4212 context,
3910 context->serial_address, 4213 context->serial_address,
3911 context->tmp_buf_a, context->buf_a_off + 8, 4214 context->tmp_buf_a, context->buf_a_off + 8,
3912 context->col_2 4215 context->col_2
3913 ); 4216 );
4217 if (context->flags & FLAG_DMA_RUN) {
4218 run_dma_src(context, -1);
4219 }
4220 context->hslot++;
4221 context->cycles += slot_cycles;
4222 vdp_advance_line(context);
4223 CHECK_ONLY
4224 }
4225 default:
4226 context->hslot++;
4227 context->cycles += MCLKS_SLOT_H32;
4228 }
4229 }
4230
4231 static void vdp_h32_phony(vdp_context * context, uint32_t target_cycles)
4232 {
4233 uint32_t const slot_cycles = MCLKS_SLOT_H32;
4234 switch(context->hslot)
4235 {
4236 for (;;)
4237 {
4238 case 133:
4239 if (context->state == PREPARING) {
4240 external_slot(context);
4241 } else {
4242 render_sprite_cells(context);
4243 }
4244 CHECK_LIMIT
4245 case 134:
4246 if (context->state == PREPARING) {
4247 external_slot(context);
4248 } else {
4249 render_sprite_cells(context);
4250 }
4251 if (context->vcounter == context->inactive_start) {
4252 context->hslot++;
4253 context->cycles += slot_cycles;
4254 return;
4255 }
4256 CHECK_LIMIT
4257 //sprite attribute table scan starts
4258 case 135:
4259 context->sprite_index = 0x80;
4260 context->slot_counter = 0;
4261 render_sprite_cells(context);
4262 scan_sprite_table(context->vcounter, context);
4263 CHECK_LIMIT
4264 SPRITE_RENDER_H32_PHONY(136)
4265 SPRITE_RENDER_H32_PHONY(137)
4266 SPRITE_RENDER_H32_PHONY(138)
4267 SPRITE_RENDER_H32_PHONY(139)
4268 SPRITE_RENDER_H32_PHONY(140)
4269 SPRITE_RENDER_H32_PHONY(141)
4270 SPRITE_RENDER_H32_PHONY(142)
4271 SPRITE_RENDER_H32_PHONY(143)
4272 SPRITE_RENDER_H32_PHONY(144)
4273 case 145:
4274 external_slot(context);
4275 CHECK_LIMIT
4276 SPRITE_RENDER_H32_PHONY(146)
4277 SPRITE_RENDER_H32_PHONY(147)
4278 SPRITE_RENDER_H32_PHONY(233)
4279 SPRITE_RENDER_H32_PHONY(234)
4280 SPRITE_RENDER_H32_PHONY(235)
4281 //HSYNC start
4282 SPRITE_RENDER_H32_PHONY(236)
4283 SPRITE_RENDER_H32_PHONY(237)
4284 SPRITE_RENDER_H32_PHONY(238)
4285 SPRITE_RENDER_H32_PHONY(239)
4286 SPRITE_RENDER_H32_PHONY(240)
4287 SPRITE_RENDER_H32_PHONY(241)
4288 SPRITE_RENDER_H32_PHONY(242)
4289 case 243:
4290 external_slot(context);
4291 CHECK_LIMIT
4292 case 244:
4293 CHECK_LIMIT //provides "garbage" for border when plane A selected
4294 SPRITE_RENDER_H32_PHONY(245)
4295 SPRITE_RENDER_H32_PHONY(246)
4296 SPRITE_RENDER_H32_PHONY(247) //provides "garbage" for border when plane B selected
4297 SPRITE_RENDER_H32_PHONY(248) //provides "garbage" for border when plane B selected
4298 //!HSYNC high
4299 case 249:
4300 CHECK_LIMIT
4301 SPRITE_RENDER_H32_PHONY(250)
4302 case 251:
4303 scan_sprite_table(context->vcounter, context);//Just a guess
4304 CHECK_LIMIT
4305 case 252:
4306 scan_sprite_table(context->vcounter, context);//Just a guess
4307 CHECK_LIMIT
4308 case 253:
4309 CHECK_LIMIT
4310 case 254:
4311 render_sprite_cells(context);
4312 scan_sprite_table(context->vcounter, context);
4313 CHECK_LIMIT
4314 case 255:
4315 scan_sprite_table(context->vcounter, context);//Just a guess
4316 CHECK_LIMIT
4317 case 0:
4318 scan_sprite_table(context->vcounter, context);//Just a guess
4319 //reverse context slot counter so it counts the number of sprite slots
4320 //filled rather than the number of available slots
4321 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
4322 context->cur_slot = context->slot_counter;
4323 context->sprite_x_offset = 0;
4324 context->sprite_draws = MAX_SPRITES_LINE_H32;
4325 CHECK_LIMIT
4326 COLUMN_RENDER_BLOCK_PHONY(2, 1)
4327 COLUMN_RENDER_BLOCK_PHONY(4, 9)
4328 COLUMN_RENDER_BLOCK_PHONY(6, 17)
4329 COLUMN_RENDER_BLOCK_REFRESH_PHONY(8, 25)
4330 COLUMN_RENDER_BLOCK_PHONY(10, 33)
4331 COLUMN_RENDER_BLOCK_PHONY(12, 41)
4332 COLUMN_RENDER_BLOCK_PHONY(14, 49)
4333 COLUMN_RENDER_BLOCK_REFRESH_PHONY(16, 57)
4334 COLUMN_RENDER_BLOCK_PHONY(18, 65)
4335 COLUMN_RENDER_BLOCK_PHONY(20, 73)
4336 COLUMN_RENDER_BLOCK_PHONY(22, 81)
4337 COLUMN_RENDER_BLOCK_REFRESH_PHONY(24, 89)
4338 COLUMN_RENDER_BLOCK_PHONY(26, 97)
4339 COLUMN_RENDER_BLOCK_PHONY(28, 105)
4340 COLUMN_RENDER_BLOCK_PHONY(30, 113)
4341 COLUMN_RENDER_BLOCK_REFRESH_PHONY(32, 121)
4342 case 129:
4343 external_slot(context);
4344 CHECK_LIMIT
4345 case 130: {
4346 external_slot(context);
4347 CHECK_LIMIT
4348 }
4349 //sprite render to line buffer starts
4350 case 131:
4351 context->cur_slot = MAX_SPRITES_LINE_H32-1;
4352 context->flags &= ~FLAG_MASKED;
4353 while (context->sprite_draws) {
4354 context->sprite_draws--;
4355 context->sprite_draw_list[context->sprite_draws].x_pos = 0;
4356 }
4357 render_sprite_cells(context);
4358 CHECK_LIMIT
4359 case 132:
4360 render_sprite_cells(context);
3914 if (context->flags & FLAG_DMA_RUN) { 4361 if (context->flags & FLAG_DMA_RUN) {
3915 run_dma_src(context, -1); 4362 run_dma_src(context, -1);
3916 } 4363 }
3917 context->hslot++; 4364 context->hslot++;
3918 context->cycles += slot_cycles; 4365 context->cycles += slot_cycles;
4952 } 5399 }
4953 } 5400 }
4954 } 5401 }
4955 } 5402 }
4956 5403
4957 void vdp_run_context_full(vdp_context * context, uint32_t target_cycles) 5404 static void vdp_inactive_phony(vdp_context *context, uint32_t target_cycles, uint8_t is_h40, uint8_t mode_5)
4958 { 5405 {
4959 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40; 5406 uint8_t buf_clear_slot, index_reset_slot, bg_end_slot, vint_slot, line_change, jump_start, jump_dest, latch_slot;
4960 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5; 5407 uint8_t index_reset_value, max_draws, max_sprites;
5408 uint16_t vint_line, active_line;
5409
5410 if (mode_5) {
5411 if (is_h40) {
5412 latch_slot = 165;
5413 buf_clear_slot = 163;
5414 index_reset_slot = 167;
5415 bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2;
5416 max_draws = MAX_SPRITES_LINE-1;
5417 max_sprites = MAX_SPRITES_LINE;
5418 index_reset_value = 0x80;
5419 vint_slot = VINT_SLOT_H40;
5420 line_change = LINE_CHANGE_H40;
5421 jump_start = 182;
5422 jump_dest = 229;
5423 } else {
5424 bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2;
5425 max_draws = MAX_SPRITES_LINE_H32-1;
5426 max_sprites = MAX_SPRITES_LINE_H32;
5427 buf_clear_slot = 128;
5428 index_reset_slot = 132;
5429 index_reset_value = 0x80;
5430 vint_slot = VINT_SLOT_H32;
5431 line_change = LINE_CHANGE_H32;
5432 jump_start = 147;
5433 jump_dest = 233;
5434 latch_slot = 243;
5435 }
5436 vint_line = context->inactive_start;
5437 active_line = 0x1FF;
5438 if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
5439 latch_slot = 220;
5440 }
5441 } else {
5442 latch_slot = 220;
5443 bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2;
5444 max_draws = MAX_DRAWS_H32_MODE4;
5445 max_sprites = 8;
5446 buf_clear_slot = 136;
5447 index_reset_slot = 253;
5448 index_reset_value = 0;
5449 vint_line = context->inactive_start + 1;
5450 vint_slot = VINT_SLOT_MODE4;
5451 line_change = LINE_CHANGE_MODE4;
5452 jump_start = 147;
5453 jump_dest = 233;
5454 if ((context->regs[REG_MODE_1] & BIT_MODE_4) || context->type != VDP_GENESIS) {
5455 active_line = 0x1FF;
5456 } else {
5457 //never active unless either mode 4 or mode 5 is turned on
5458 active_line = 0x200;
5459 }
5460 }
5461
4961 while(context->cycles < target_cycles) 5462 while(context->cycles < target_cycles)
4962 { 5463 {
4963 check_switch_inactive(context, is_h40); 5464 check_switch_inactive(context, is_h40);
4964 5465 //this will need some tweaking to properly interact with 128K mode,
4965 if (is_active(context)) { 5466 //but this should be good enough for now
5467 context->serial_address += 1024;
5468
5469 if (context->hslot == buf_clear_slot) {
4966 if (mode_5) { 5470 if (mode_5) {
4967 if (is_h40) { 5471 context->cur_slot = max_draws;
4968 vdp_h40(context, target_cycles); 5472 } else if ((context->regs[REG_MODE_1] & BIT_MODE_4) || context->type == VDP_GENESIS) {
5473 context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1;
5474 context->sprite_draws = MAX_DRAWS_H32_MODE4;
5475 } else {
5476 context->sprite_draws = 0;
5477 }
5478 memset(context->linebuf, 0, LINEBUF_SIZE);
5479 } else if (context->hslot == index_reset_slot) {
5480 context->sprite_index = index_reset_value;
5481 context->slot_counter = mode_5 ? 0 : max_sprites;
5482 } else if (context->vcounter == vint_line && context->hslot == vint_slot) {
5483 context->flags2 |= FLAG2_VINT_PENDING;
5484 context->pending_vint_start = context->cycles;
5485 } else if (context->vcounter == context->inactive_start && context->hslot == 1 && (context->regs[REG_MODE_4] & BIT_INTERLACE)) {
5486 context->flags2 ^= FLAG2_EVEN_FIELD;
5487 }
5488
5489 if (!is_refresh(context, context->hslot)) {
5490 external_slot(context);
5491 if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) {
5492 run_dma_src(context, context->hslot);
5493 }
5494 }
5495
5496 if (is_h40) {
5497 if (context->hslot >= HSYNC_SLOT_H40 && context->hslot < HSYNC_END_H40) {
5498 context->cycles += h40_hsync_cycles[context->hslot - HSYNC_SLOT_H40];
5499 } else {
5500 context->cycles += MCLKS_SLOT_H40;
5501 }
5502 } else {
5503 context->cycles += MCLKS_SLOT_H32;
5504 }
5505 if (context->hslot == jump_start) {
5506 context->hslot = jump_dest;
5507 } else {
5508 context->hslot++;
5509 }
5510 if (context->hslot == line_change) {
5511 vdp_advance_line(context);
5512 if (context->vcounter == active_line) {
5513 context->state = PREPARING;
5514 return;
5515 }
5516 }
5517 }
5518 }
5519
5520 void vdp_run_context_full(vdp_context * context, uint32_t target_cycles)
5521 {
5522 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40;
5523 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
5524 if (context->renderer) {
5525 while(context->cycles < target_cycles)
5526 {
5527 check_switch_inactive(context, is_h40);
5528
5529 if (is_active(context)) {
5530 if (mode_5) {
5531 if (is_h40) {
5532 vdp_h40_phony(context, target_cycles);
5533 } else {
5534 vdp_h32_phony(context, target_cycles);
5535 }
5536 } else if (context->regs[REG_MODE_1] & BIT_MODE_4) {
5537 //TODO: phonyfy this
5538 vdp_h32_mode4(context, target_cycles);
5539 } else if (context->regs[REG_MODE_2] & BIT_M1) {
5540 vdp_tms_text(context, target_cycles);
4969 } else { 5541 } else {
4970 vdp_h32(context, target_cycles); 5542 vdp_tms_graphics(context, target_cycles);
4971 } 5543 }
4972 } else if (context->regs[REG_MODE_1] & BIT_MODE_4) {
4973 vdp_h32_mode4(context, target_cycles);
4974 } else if (context->regs[REG_MODE_2] & BIT_M1) {
4975 vdp_tms_text(context, target_cycles);
4976 } else { 5544 } else {
4977 vdp_tms_graphics(context, target_cycles); 5545 vdp_inactive_phony(context, target_cycles, is_h40, mode_5);
4978 } 5546 }
4979 } else { 5547 }
4980 vdp_inactive(context, target_cycles, is_h40, mode_5); 5548 } else {
5549 while(context->cycles < target_cycles)
5550 {
5551 check_switch_inactive(context, is_h40);
5552
5553 if (is_active(context)) {
5554 if (mode_5) {
5555 if (is_h40) {
5556 vdp_h40(context, target_cycles);
5557 } else {
5558 vdp_h32(context, target_cycles);
5559 }
5560 } else if (context->regs[REG_MODE_1] & BIT_MODE_4) {
5561 vdp_h32_mode4(context, target_cycles);
5562 } else if (context->regs[REG_MODE_2] & BIT_M1) {
5563 vdp_tms_text(context, target_cycles);
5564 } else {
5565 vdp_tms_graphics(context, target_cycles);
5566 }
5567 } else {
5568 vdp_inactive(context, target_cycles, is_h40, mode_5);
5569 }
4981 } 5570 }
4982 } 5571 }
4983 } 5572 }
4984 5573
4985 void vdp_run_context(vdp_context *context, uint32_t target_cycles) 5574 void vdp_run_context(vdp_context *context, uint32_t target_cycles)