comparison vdp.c @ 1335:26e72126f9d1

Fixes to sprite phase 2 so that sprite X reads use the exact same slot as on hardware in the case that there are fewer than the max number of sprites on each line. Re-read sprite Y from SAT cache during phase 2 and properly mask the calculated row. Fixes remaining issues with spinning cube scene in Overdrive 2.
author Michael Pavone <pavone@retrodev.com>
date Thu, 27 Apr 2017 23:08:49 -0700
parents 7757d605e365
children d092c15246a3
comparison
equal deleted inserted replaced
1334:7757d605e365 1335:26e72126f9d1
95 context->border_bot = BORDER_BOT_V28_PAL; 95 context->border_bot = BORDER_BOT_V28_PAL;
96 } else { 96 } else {
97 context->border_top = BORDER_TOP_V28; 97 context->border_top = BORDER_TOP_V28;
98 context->border_bot = BORDER_TOP_V28; 98 context->border_bot = BORDER_TOP_V28;
99 } 99 }
100 }
101 if (context->regs[REG_MODE_4] & BIT_H40) {
102 context->max_sprites_frame = MAX_SPRITES_FRAME;
103 context->max_sprites_line = MAX_SPRITES_LINE;
104 } else {
105 context->max_sprites_frame = MAX_SPRITES_FRAME_H32;
106 context->max_sprites_line = MAX_SPRITES_LINE_H32;
100 } 107 }
101 if (context->state == INACTIVE) { 108 if (context->state == INACTIVE) {
102 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active 109 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
103 if (context->vcounter < context->inactive_start) { 110 if (context->vcounter < context->inactive_start) {
104 context->state = ACTIVE; 111 context->state = ACTIVE;
542 return context->state != INACTIVE && (context->regs[REG_MODE_2] & BIT_DISP_EN) != 0; 549 return context->state != INACTIVE && (context->regs[REG_MODE_2] & BIT_DISP_EN) != 0;
543 } 550 }
544 551
545 static void scan_sprite_table(uint32_t line, vdp_context * context) 552 static void scan_sprite_table(uint32_t line, vdp_context * context)
546 { 553 {
547 if (context->sprite_index && context->slot_counter) { 554 if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line) {
548 line += 1; 555 line += 1;
549 line &= 0xFF; 556 line &= 0xFF;
550 uint16_t ymask, ymin; 557 uint16_t ymask, ymin;
551 uint8_t height_mult; 558 uint8_t height_mult;
552 if (context->double_res) { 559 if (context->double_res) {
561 ymask = 0x1FF; 568 ymask = 0x1FF;
562 ymin = 128; 569 ymin = 128;
563 height_mult = 8; 570 height_mult = 8;
564 } 571 }
565 context->sprite_index &= 0x7F; 572 context->sprite_index &= 0x7F;
566 if (context->regs[REG_MODE_4] & BIT_H40) { 573 //TODO: Implement squirelly behavior documented by Kabuto
567 if (context->sprite_index >= MAX_SPRITES_FRAME) { 574 if (context->sprite_index >= context->max_sprites_frame) {
568 context->sprite_index = 0;
569 return;
570 }
571 } else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) {
572 context->sprite_index = 0; 575 context->sprite_index = 0;
573 return; 576 return;
574 } 577 }
575 uint16_t address = context->sprite_index * 4; 578 uint16_t address = context->sprite_index * 4;
576 line += ymin; 579 line += ymin;
577 uint16_t y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask; 580 uint16_t y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask;
578 uint8_t height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult; 581 uint8_t height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult;
579 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height); 582 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
580 if (y <= line && line < (y + height)) { 583 if (y <= line && line < (y + height)) {
581 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line); 584 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
582 context->sprite_info_list[--(context->slot_counter)].size = context->sat_cache[address+2]; 585 context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
583 context->sprite_info_list[context->slot_counter].index = context->sprite_index; 586 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
584 context->sprite_info_list[context->slot_counter].y = y-ymin; 587 context->sprite_info_list[context->slot_counter++].y = y-ymin;
585 } 588 }
586 context->sprite_index = context->sat_cache[address+3] & 0x7F; 589 context->sprite_index = context->sat_cache[address+3] & 0x7F;
587 if (context->sprite_index && context->slot_counter) 590 if (context->sprite_index && ((uint8_t)context->slot_counter) < context->max_sprites_line)
588 { 591 {
589 if (context->regs[REG_MODE_4] & BIT_H40) { 592 //TODO: Implement squirelly behavior documented by Kabuto
590 if (context->sprite_index >= MAX_SPRITES_FRAME) { 593 if (context->sprite_index >= context->max_sprites_frame) {
591 context->sprite_index = 0;
592 return;
593 }
594 } else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) {
595 context->sprite_index = 0; 594 context->sprite_index = 0;
596 return; 595 return;
597 } 596 }
598 address = context->sprite_index * 4; 597 address = context->sprite_index * 4;
599 y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask; 598 y = ((context->sat_cache[address] & 0x3) << 8 | context->sat_cache[address+1]) & ymask;
600 height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult; 599 height = ((context->sat_cache[address+2] & 0x3) + 1) * height_mult;
601 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height); 600 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
602 if (y <= line && line < (y + height)) { 601 if (y <= line && line < (y + height)) {
603 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line); 602 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
604 context->sprite_info_list[--(context->slot_counter)].size = context->sat_cache[address+2]; 603 context->sprite_info_list[context->slot_counter].size = context->sat_cache[address+2];
605 context->sprite_info_list[context->slot_counter].index = context->sprite_index; 604 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
606 context->sprite_info_list[context->slot_counter].y = y-ymin; 605 context->sprite_info_list[context->slot_counter++].y = y-ymin;
607 } 606 }
608 context->sprite_index = context->sat_cache[address+3] & 0x7F; 607 context->sprite_index = context->sat_cache[address+3] & 0x7F;
609 } 608 }
610 } 609 }
610 //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
611 } 611 }
612 612
613 static void scan_sprite_table_mode4(vdp_context * context) 613 static void scan_sprite_table_mode4(vdp_context * context)
614 { 614 {
615 if (context->sprite_index < MAX_SPRITES_FRAME_H32) { 615 if (context->sprite_index < MAX_SPRITES_FRAME_H32) {
630 context->flags |= FLAG_DOT_OFLOW; 630 context->flags |= FLAG_DOT_OFLOW;
631 return; 631 return;
632 } 632 }
633 context->sprite_info_list[--(context->slot_counter)].size = size; 633 context->sprite_info_list[--(context->slot_counter)].size = size;
634 context->sprite_info_list[context->slot_counter].index = context->sprite_index; 634 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
635 context->sprite_info_list[context->slot_counter].y = y;
636 } 635 }
637 context->sprite_index++; 636 context->sprite_index++;
638 } 637 }
639 638
640 if (context->sprite_index < MAX_SPRITES_FRAME_H32) { 639 if (context->sprite_index < MAX_SPRITES_FRAME_H32) {
649 context->flags |= FLAG_DOT_OFLOW; 648 context->flags |= FLAG_DOT_OFLOW;
650 return; 649 return;
651 } 650 }
652 context->sprite_info_list[--(context->slot_counter)].size = size; 651 context->sprite_info_list[--(context->slot_counter)].size = size;
653 context->sprite_info_list[context->slot_counter].index = context->sprite_index; 652 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
654 context->sprite_info_list[context->slot_counter].y = y;
655 } 653 }
656 context->sprite_index++; 654 context->sprite_index++;
657 } 655 }
658 } 656 }
659 657
660 } 658 }
661 } 659 }
662 660
663 static void read_sprite_x(uint32_t line, vdp_context * context) 661 static void read_sprite_x(uint32_t line, vdp_context * context)
664 { 662 {
665 if (context->cur_slot >= context->slot_counter) { 663 if (context->cur_slot == context->max_sprites_line) {
664 context->cur_slot = 0;
665 }
666 if (context->cur_slot < context->slot_counter) {
666 if (context->sprite_draws) { 667 if (context->sprite_draws) {
667 line += 1; 668 line += 1;
668 line &= 0xFF; 669 line &= 0xFF;
669 //in tiles 670 //in tiles
670 uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1; 671 uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1;
675 if (context->flags2 & FLAG2_EVEN_FIELD) { 676 if (context->flags2 & FLAG2_EVEN_FIELD) {
676 line++; 677 line++;
677 } 678 }
678 height *= 2; 679 height *= 2;
679 } 680 }
681 uint16_t ymask, ymin;
682 if (context->double_res) {
683 ymask = 0x3FF;
684 ymin = 256;
685 } else {
686 ymask = 0x1FF;
687 ymin = 128;
688 }
680 uint16_t att_addr = mode5_sat_address(context) + context->sprite_info_list[context->cur_slot].index * 8 + 4; 689 uint16_t att_addr = mode5_sat_address(context) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
681 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; 690 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
682 uint8_t pal_priority = (tileinfo >> 9) & 0x70; 691 uint8_t pal_priority = (tileinfo >> 9) & 0x70;
683 uint8_t row; 692 uint8_t row;
693 uint16_t cache_addr = context->sprite_info_list[context->cur_slot].index * 4;
694 int16_t y = ((context->sat_cache[cache_addr] << 8 | context->sat_cache[cache_addr+1]) & ymask) - ymin;
684 if (tileinfo & MAP_BIT_V_FLIP) { 695 if (tileinfo & MAP_BIT_V_FLIP) {
685 row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line; 696 row = (y + height - 1) - line;
686 } else { 697 } else {
687 row = line-context->sprite_info_list[context->cur_slot].y; 698 row = line-y;
688 } 699 }
700 row &= ymask >> 4;
689 uint16_t address; 701 uint16_t address;
690 if (context->double_res) { 702 if (context->double_res) {
691 address = ((tileinfo & 0x3FF) << 6) + row * 4; 703 address = ((tileinfo & 0x3FF) << 6) + row * 4;
692 } else { 704 } else {
693 address = ((tileinfo & 0x7FF) << 5) + row * 4; 705 address = ((tileinfo & 0x7FF) << 5) + row * 4;
728 //Used to be i < width 740 //Used to be i < width
729 //TODO: Confirm this is the right condition on hardware 741 //TODO: Confirm this is the right condition on hardware
730 if (!context->sprite_draws) { 742 if (!context->sprite_draws) {
731 context->flags |= FLAG_DOT_OFLOW; 743 context->flags |= FLAG_DOT_OFLOW;
732 } 744 }
733 context->cur_slot--;
734 } else { 745 } else {
735 context->flags |= FLAG_DOT_OFLOW; 746 context->flags |= FLAG_DOT_OFLOW;
736 } 747 }
737 } 748 }
749 context->cur_slot++;
738 } 750 }
739 751
740 static void read_sprite_x_mode4(vdp_context * context) 752 static void read_sprite_x_mode4(vdp_context * context)
741 { 753 {
742 if (context->cur_slot >= context->slot_counter) { 754 if (context->cur_slot >= context->slot_counter) {
1897 { 1909 {
1898 *(dst++) = bg_color; 1910 *(dst++) = bg_color;
1899 } 1911 }
1900 } 1912 }
1901 context->sprite_index = 0x80; 1913 context->sprite_index = 0x80;
1902 context->slot_counter = MAX_SPRITES_LINE; 1914 context->slot_counter = 0;
1903 render_sprite_cells( context); 1915 render_sprite_cells( context);
1904 scan_sprite_table(context->vcounter, context); 1916 scan_sprite_table(context->vcounter, context);
1905 CHECK_LIMIT 1917 CHECK_LIMIT
1906 SPRITE_RENDER_H40(168) 1918 SPRITE_RENDER_H40(168)
1907 SPRITE_RENDER_H40(169) 1919 SPRITE_RENDER_H40(169)
1979 scan_sprite_table(context->vcounter, context);//Just a guess 1991 scan_sprite_table(context->vcounter, context);//Just a guess
1980 CHECK_LIMIT 1992 CHECK_LIMIT
1981 case 0: 1993 case 0:
1982 render_map_output(context->vcounter, 0, context); 1994 render_map_output(context->vcounter, 0, context);
1983 scan_sprite_table(context->vcounter, context);//Just a guess 1995 scan_sprite_table(context->vcounter, context);//Just a guess
1984 //reverse context slot counter so it counts the number of sprite slots 1996 //seems like the sprite table scan fills a shift register
1985 //filled rather than the number of available slots 1997 //values are FIFO, but unused slots precede used slots
1986 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; 1998 //so we set cur_slot to slot_counter and let it wrap around to
1987 context->cur_slot = MAX_SPRITES_LINE-1; 1999 //the beginning of the list
2000 context->cur_slot = context->slot_counter;
1988 context->sprite_draws = MAX_DRAWS; 2001 context->sprite_draws = MAX_DRAWS;
1989 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); 2002 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
1990 CHECK_LIMIT 2003 CHECK_LIMIT
1991 COLUMN_RENDER_BLOCK(2, 1) 2004 COLUMN_RENDER_BLOCK(2, 1)
1992 COLUMN_RENDER_BLOCK(4, 9) 2005 COLUMN_RENDER_BLOCK(4, 9)
2104 { 2117 {
2105 *(dst++) = bg_color; 2118 *(dst++) = bg_color;
2106 } 2119 }
2107 } 2120 }
2108 context->sprite_index = 0x80; 2121 context->sprite_index = 0x80;
2109 context->slot_counter = MAX_SPRITES_LINE_H32; 2122 context->slot_counter = 0;
2110 render_sprite_cells( context); 2123 render_sprite_cells( context);
2111 scan_sprite_table(context->vcounter, context); 2124 scan_sprite_table(context->vcounter, context);
2112 CHECK_LIMIT 2125 CHECK_LIMIT
2113 SPRITE_RENDER_H32(136) 2126 SPRITE_RENDER_H32(136)
2114 SPRITE_RENDER_H32(137) 2127 SPRITE_RENDER_H32(137)
2184 render_map_output(context->vcounter, 0, context); 2197 render_map_output(context->vcounter, 0, context);
2185 scan_sprite_table(context->vcounter, context);//Just a guess 2198 scan_sprite_table(context->vcounter, context);//Just a guess
2186 //reverse context slot counter so it counts the number of sprite slots 2199 //reverse context slot counter so it counts the number of sprite slots
2187 //filled rather than the number of available slots 2200 //filled rather than the number of available slots
2188 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; 2201 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
2189 context->cur_slot = MAX_SPRITES_LINE_H32-1; 2202 context->cur_slot = context->slot_counter;
2190 context->sprite_draws = MAX_DRAWS_H32; 2203 context->sprite_draws = MAX_DRAWS_H32;
2191 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); 2204 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
2192 CHECK_LIMIT 2205 CHECK_LIMIT
2193 COLUMN_RENDER_BLOCK(2, 1) 2206 COLUMN_RENDER_BLOCK(2, 1)
2194 COLUMN_RENDER_BLOCK(4, 9) 2207 COLUMN_RENDER_BLOCK(4, 9)
2374 uint16_t vint_line, active_line; 2387 uint16_t vint_line, active_line;
2375 uint32_t bg_color; 2388 uint32_t bg_color;
2376 2389
2377 if (mode_5) { 2390 if (mode_5) {
2378 if (is_h40) { 2391 if (is_h40) {
2379 buf_clear_slot = 161; 2392 buf_clear_slot = 163;
2380 index_reset_slot = 165; 2393 index_reset_slot = 167;
2381 bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2; 2394 bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2;
2382 max_draws = MAX_DRAWS-1; 2395 max_draws = MAX_DRAWS-1;
2383 max_sprites = MAX_SPRITES_LINE; 2396 max_sprites = MAX_SPRITES_LINE;
2384 index_reset_value = 0x80; 2397 index_reset_value = 0x80;
2385 vint_slot = VINT_SLOT_H40; 2398 vint_slot = VINT_SLOT_H40;
2448 context->sprite_draws = MAX_DRAWS_H32_MODE4; 2461 context->sprite_draws = MAX_DRAWS_H32_MODE4;
2449 } 2462 }
2450 memset(context->linebuf, 0, LINEBUF_SIZE); 2463 memset(context->linebuf, 0, LINEBUF_SIZE);
2451 } else if (context->hslot == index_reset_slot) { 2464 } else if (context->hslot == index_reset_slot) {
2452 context->sprite_index = index_reset_value; 2465 context->sprite_index = index_reset_value;
2453 context->slot_counter = max_sprites; 2466 context->slot_counter = mode_5 ? 0 : max_sprites;
2454 } else if (context->vcounter == vint_line && context->hslot == vint_slot) { 2467 } else if (context->vcounter == vint_line && context->hslot == vint_slot) {
2455 context->flags2 |= FLAG2_VINT_PENDING; 2468 context->flags2 |= FLAG2_VINT_PENDING;
2456 context->pending_vint_start = context->cycles; 2469 context->pending_vint_start = context->cycles;
2457 } 2470 }
2458 2471
2660 context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); 2673 context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
2661 if (!context->double_res) { 2674 if (!context->double_res) {
2662 context->flags2 &= ~FLAG2_EVEN_FIELD; 2675 context->flags2 &= ~FLAG2_EVEN_FIELD;
2663 } 2676 }
2664 } 2677 }
2665 if (reg == REG_MODE_2) { 2678 if (reg == REG_MODE_1 || reg == REG_MODE_2 || reg == REG_MODE_4) {
2666 update_video_params(context); 2679 update_video_params(context);
2667 } 2680 }
2668 } 2681 }
2669 } else if (mode_5) { 2682 } else if (mode_5) {
2670 context->flags |= FLAG_PENDING; 2683 context->flags |= FLAG_PENDING;