Mercurial > repos > blastem
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; |